Railway Operation Simulator  v2.11.0
A railway simulator for Windows
TrainUnit.cpp
Go to the documentation of this file.
1 // TrainUnit.cpp
2 /*
3  BEWARE OF COMMENTS in .cpp files: they were accurate when written but have
4  sometimes been overtaken by changes and not updated
5  Comments in .h files are believed to be accurate and up to date
6 
7  This is a source code file for "railway.exe", a railway operation
8  simulator, written originally in Borland C++ Builder 4 Professional with
9  later updates in Embarcadero C++Builder 10.2.
10  Copyright (C) 2010 Albert Ball [original development]
11 
12  This program is free software: you can redistribute it and/or modify
13  it under the terms of the GNU General Public License as published by
14  the Free Software Foundation, either version 3 of the License, or
15  (at your option) any later version.
16 
17  This program is distributed in the hope that it will be useful,
18  but WITHOUT ANY WARRANTY; without even the implied warranty of
19  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20  GNU General Public License for more details.
21 
22  You should have received a copy of the GNU General Public License
23  along with this program. If not, see <http://www.gnu.org/licenses/>.
24 */
25 // ---------------------------------------------------------------------------
26 #include <Classes.hpp>
27 #include <Controls.hpp>
28 #include <StdCtrls.hpp>
29 #include <Forms.hpp>
30 #include <Buttons.hpp>
31 #include <ExtCtrls.hpp>
32 #include <Menus.hpp>
33 #include <Dialogs.hpp>
34 #include <Graphics.hpp>
35 #include <ComCtrls.hpp>
36 #include <fstream>
37 #include <vector>
38 #include <algorithm> //for sort
39 #include <vcl.h>
40 #include <stdlib.h> //for rand()
41 #include <math.hpp> //for speed & performance calcs
42 
43 #pragma hdrstop
44 
45 #include "TrainUnit.h"
46 #include "TrackUnit.h"
47 #include "GraphicUnit.h"
48 #include "DisplayUnit.h"
49 #include "Utilities.h"
50 
51 // ---------------------------------------------------------------------------
52 #pragma package(smart_init)
53 
55 
56 // ---------------------------------------------------------------------------
57 
58 int TTrain::NextTrainID = 0; // has to be initialised outside the class
59 
60 // ---------------------------------------------------------------------------
61 
62 TExitInfo::TExitInfo() //default constructor
63 {
64  ServiceReference = " ";
65  RepeatNumber = 0;
66  TimeToExitSecs = -1;
67 }
68 
69 // ---------------------------------------------------------------------------
70 
71 TTrain::TTrain(int Caller, int RearStartElementIn, int RearStartExitPosIn, AnsiString InputCode, int StartSpeedIn, int MassIn, double MaxRunningSpeedIn,
72  double MaxBrakeRateIn, double PowerAtRailIn, TTrainMode TrainModeIn, TTrainDataEntry *TrainDataEntryPtrIn, int RepeatNumberIn, int IncrementalMinutesIn,
73  int IncrementalDigitsIn, int SignallerMaxSpeedIn) : RearStartElement(RearStartElementIn), RearStartExitPos(RearStartExitPosIn), HeadCode(InputCode),
74  StartSpeed(StartSpeedIn), Mass(MassIn), MaxRunningSpeed(MaxRunningSpeedIn), MaxBrakeRate(MaxBrakeRateIn), PowerAtRail(PowerAtRailIn),
75  TrainMode(TrainModeIn), TrainDataEntryPtr(TrainDataEntryPtrIn), RepeatNumber(RepeatNumberIn), IncrementalMinutes(IncrementalMinutesIn),
76  IncrementalDigits(IncrementalDigitsIn), SignallerMaxSpeed(SignallerMaxSpeedIn)
77 /*
78  Construct a new train with general default values and input values for position and headcode.
79  Create the frontcode, headcode and background graphics here but don't delete them in a destructor.
80  This is because trains are kept in a vector and vectors erase elements during internal operations.
81  Deletion is explicit by using a special function. Increment the static class member NextTrainID
82  after setting this train's ID.
83 */
84 
85 {
86  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TTrain," + AnsiString(RearStartElementIn) + "," +
87  AnsiString(RearStartExitPosIn) + "," + AnsiString(InputCode) + "," + AnsiString(StartSpeedIn) + "," + AnsiString(MassIn) + "," +
88  AnsiString(TrainModeIn));
89  // AutoControl = true;//all trains start in auto control
90  UpdateCounter = 0;
91  TimeTimeLocArrived = false;
92  Derailed = false;
93  DerailPending = false;
94  Crashed = false;
95  StoppedAtBuffers = false;
96  StoppedAtSignal = false;
97  StoppedAtLocation = false;
98  StoppedAfterSPAD = false;
99  StoppedWithoutPower = false; // new at v2.4.0
100  StoppedForTrainInFront = false;
101  SignallerStoppingFlag = false;
102  SignallerStopped = false;
103  SignallerRemoved = false;
104  NotInService = false;
105  HoldAtLocationInTTMode = false;
106  AllowedToPassRedSignal = false;
107  CallingOnFlag = false;
108  BeingCalledOn = false;
109  DepartureTimeSet = false;
111  TimetableFinished = false;
112  LastActionDelayFlag = false;
113  OneLengthAccelDecel = false;
114  TrainCrashedInto = -1;
116  Plotted = false;
117  TrainGone = false;
118  SPADFlag = false;
119  FrontCodePtr = new Graphics::TBitmap;
120  FrontCodePtr->PixelFormat = pf8bit;
121  FrontCodePtr->Height = 8;
122  FrontCodePtr->Width = 8;
124  FrontCodePtr->Transparent = false;
125  AValue = sqrt(2 * PowerAtRail / Mass);
127  TerminatedMessageSent = false;
128  JoinedOtherTrainFlag = false;
130  StepForwardFlag = false;
132  for(int x = 0; x < 4; x++)
133  {
134  HeadCodeGrPtr[x] = new Graphics::TBitmap;
135  HeadCodeGrPtr[x]->PixelFormat = pf8bit;
136  HeadCodeGrPtr[x]->Height = 8;
137  HeadCodeGrPtr[x]->Width = 8;
139  HeadCodeGrPtr[x]->Transparent = false;
140  }
141  for(int x = 0; x < 4; x++)
142  {
143  BackgroundPtr[x] = new Graphics::TBitmap;
144  BackgroundPtr[x]->PixelFormat = pf8bit;
145  BackgroundPtr[x]->Height = 8;
146  BackgroundPtr[x]->Width = 8;
148  BackgroundPtr[x]->Transparent = false;
149  }
150  for(int x = 0; x < 4; x++)
151  {
153  // set here to ensure have values
154  }
155  for(int x = 0; x < 4; x++)
156  {
157  PlotElement[x] = -1; // marker for not plotted yet
158  }
159  for(int x = 0; x < 3; x++)
160  {
161  OldZoomOutElement[x] = -1; // marker for not plotted yet
162  }
164  NextTrainID++;
165 
166  // new values added to complete initialisation of all TTrain variables
167 
168  // ActionVectorEntryPtr = &(TrainDataEntryPtr->ActionVector.at(0)); can't be initialised yet as session trains created with Null
169  // TrainDataEntryPtr, initialise in AddTrain
171  FrontElementLength = 0;
172  EntrySpeed = 0;
173  ExitSpeedHalf = 0;
174  ExitSpeedFull = 0;
175  MaxExitSpeed = 0;
176  BrakeRate = 0;
178  FirstHalfMove = true;
179  EntryTime = 0;
180  ExitTimeHalf = 0;
181  ExitTimeFull = 0;
182  ReleaseTime = 0;
183  TRSTime = 0;
184  LastActionTime = 0;
185  Straddle = MidLag;
186  LeadElement = -1;
187  LeadEntryPos = 0;
188  LeadExitPos = 0;
189  MidElement = -1;
190  MidEntryPos = 0;
191  MidExitPos = 0;
192  LagElement = -1;
193  LagEntryPos = 0;
194  LagExitPos = 0;
195  TrainFailed = false; // added at v2.4.0
196  for(int x = 0; x < 4; x++)
197  {
198  HOffset[x] = 0;
199  VOffset[x] = 0;
200  PlotEntryPos[x] = 0;
201  }
202  OpTimeToAct = 60; // default value, new at v2.2.0
203  TimeToExit = -1;
204  ExitPair.first = -1;
205  ExitPair.second = -1;
206  MinsDelayed = 0.0; // new at v2.2.0
207  FirstLaterStopRecoverableTime = 0.0; // new at v2.2.0
208  FinishJoinLogSent = false;
209  // added at v2.4.0 to prevent repeatdly logging the event
212  // added at v2.4.0, no need to include in session file as will only be sent once & better that way
216  ZeroPowerNoCDTMessage = false;
221  TrainFailurePending = false;
222  SkippedDeparture = false;
223  ActionsSkippedFlag = false;
224  SkipPtrValue = 0;
225  TrainSkippedEvents = 0;
226  Utilities->CallLogPop(648);
227 }
228 
229 // ---------------------------------------------------------------------------
230 
231 void TTrain::DeleteTrain(int Caller)
232 /*
233  Delete train heap objects (bitmaps) explicitly by this special function rather than by a destructor, because vectors
234  erase elements during internal operations & if TTrain had an explicit destructor that deleted the heap elements then
235  it would be called when a vector element was erased. Calling the default TTrain destructor doesn't matter because all that
236  does is release the memory of the members (including pointers to the bitmaps), it doesn't destroy the bitmaps themselves.
237  It's important therefore to call this function before erasing the vector element, otherwise the pointers to the bitmaps
238  would be lost and the bitmaps never destroyed, thereby causing memory leaks.
239  No need to delete HeadCodePosition as that just points to existing bitmaps
240 */{
241  // if(NoDelete) return;//used when a TTrain is created to hold copied values from elsewhere
242  TrainController->LogEvent("" + AnsiString(Caller) + ",DeleteTrain," + HeadCode);
243  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",DeleteTrain," + HeadCode);
244  if(Display->ZoomOutFlag)
245  {
247  }
248  if(FrontCodePtr == 0)
249  {
250  throw Exception("Error in attempting to delete FrontCodePtr");
251  }
252  delete FrontCodePtr;
253  FrontCodePtr = 0;
254  for(int x = 0; x < 4; x++)
255  {
256  if(BackgroundPtr[x] == 0)
257  {
258  throw Exception("Error in attempting to delete BackgroundPtr[" + AnsiString(x) + "]");
259  }
260  delete BackgroundPtr[x];
261  BackgroundPtr[x] = 0;
262  }
263  for(int x = 0; x < 4; x++)
264  {
265  if(HeadCodeGrPtr[x] == 0)
266  {
267  throw Exception("Error in attempting to delete HeadCodeGrPtr[" + AnsiString(x) + "]");
268  }
269  delete HeadCodeGrPtr[x];
270  HeadCodeGrPtr[x] = 0;
271  }
272  Utilities->CallLogPop(649);
273 }
274 
275 // ---------------------------------------------------------------------------
276 
278 /*
279  Plots the train starting position on screen. Note that the check for starting on straight points &
280  on wrongly set points is carried out in TrainControllerUnit [but have to allow for starting on points because
281  ChangeDirection calls this function.]. Train starts on Lead & Mid elements & Straddle = LeadMid unless
282  entering at a continuation in which case Straddle = MidLag & train not plotted immediately.
283  Set the headcode graphics pointers from the headcode text, then check whether starting at a
284  continuation. If so set Mid & Lag elements to -1 so they won't be plotted, and set Lead values
285  for the continuation element. Otherwise set Lead and Mid values,
286 
287  and Lead element value unless
288  Mid element is a buffer or continuation. Set Straddle, then for the Mid element set the graphic
289  offsets and headcode positions and front code. Pick up background bitmaps for the Mid element,
290  then check if a train on either Mid or Lag and if so give a warning message and return false so
291  that the calling function can delete the train. Plot the Mid element train values then do similarly
292  for the Lag element - set offsets, pick up background bitmaps, and plot the rear two segments of
293  the train. Finally set the Plotted flag and return true.
294 */{
295  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PlotStartPosition," + HeadCode);
296  int NextElementPosition, NextEntryPos, ElementLength, SpeedLimit;
297 
299  // PlotStartTime = TrainController->TTClockTime;
300  FirstHalfMove = true;
301 
302  // if enter at continuation then don't plot anything at start, but set TrainIDOnElement for continuation entry so as to
303  // 'claim' it for this train to prevent any other waiting trains trying to enter
305  {
306  LagElement = -1; // not to be plotted
307  LagExitPos = 0; // not to be plotted
308  LagEntryPos = 0; // not to be plotted
309  MidElement = -1; // not to be plotted
310  MidExitPos = 0; // not to be plotted
311  MidEntryPos = 0; // not to be plotted
313  LeadExitPos = 1; // will be 1 for continuation entry
314  LeadEntryPos = 0;
315 
317  MaxExitSpeed = StartSpeed; // initial value
319  ElementLength = Track->TrackElementAt(164, LeadElement).Length01;
320  SpeedLimit = Track->TrackElementAt(165, LeadElement).SpeedLimit01;
321  if(EntrySpeed > SpeedLimit)
322  {
323  EntrySpeed = SpeedLimit;
324  }
326  {
328  }
330  // LeadElement is the element to be entered
331 
332  // Precautionary check - If need to brake EntrySpeed may be too high, so set it to the speed at which
333  // can achieve ExitSpeedFull at the half braking rate.
335  {
336  double TempEntrySpeed = sqrt((MaxExitSpeed * MaxExitSpeed) + (3.6 * 3.6 * MaxBrakeRate * ElementLength)); // half braking
337  if(TempEntrySpeed < EntrySpeed)
338  {
339  EntrySpeed = TempEntrySpeed;
341  }
342  }
343  Straddle = MidLag; // only for starting on a continuation
345  // no need to stop gap flashing if start on continuation
346  }
347  else // not starting at a continuation
348  {
349  LagElement = -1;
350  LagEntryPos = 0;
351  LagExitPos = 0;
358 
360  MaxExitSpeed = StartSpeed; // initial value
362  bool TempDerail = false; // dummy
363  NextElementPosition = Track->TrackElementAt(168, LeadElement).Conn[Track->GetAnyElementOppositeLinkPos(2, LeadElement, LeadEntryPos, TempDerail)];
365  if((PowerAtRail < 1) && EntrySpeed < 1) // added at v2.4.0
366  {
367  StoppedWithoutPower = true;
368  }
369  // facing buffers check - ignore starting speed if start facing buffers
370  StoppedAtBuffers = false;
371  // need to set here as well as in UpdateTrain() in case paused during signaller change direction
374  {
375  FrontElementSpeedLimit = Track->TrackElementAt(494, LeadElement).SpeedLimit01; // use 01 for convenience, not used
376  FrontElementLength = Track->TrackElementAt(495, LeadElement).Length01; // use 01 for convenience, not used
377  EntrySpeed = 0;
378  ExitSpeedHalf = 0;
379  ExitSpeedFull = 0;
380  MaxExitSpeed = 0;
381  // SetTrainMovementValues not called so set this here
382  BrakeRate = 0;
385  StoppedAtSignal = false;
386  // new v2.2.0: can't be at buffers and signal! If was set then won't be reset as later
387  // signal check is an 'else'
388  if(!StoppedAtLocation)
389  {
390  StoppedAtBuffers = true; // stopped at location takes precedence
391  }
392  }
393 
394  // facing continuation check - don't allow to stop even if no power
396  {
397  FrontElementSpeedLimit = Track->TrackElementAt(509, LeadElement).SpeedLimit01; // use 01 for convenience, not used
398  FrontElementLength = Track->TrackElementAt(510, LeadElement).Length01; // use 01 for convenience, not used
402  BrakeRate = 0;
403  ExitTimeHalf = TrainController->TTClockTime + TDateTime(1.8 * (double) FrontElementLength / EntrySpeed / 86400);
404  ExitTimeFull = TrainController->TTClockTime + TDateTime(3.6 * (double) FrontElementLength / EntrySpeed / 86400);
405  }
406 
407  // Signal check
408  else if((NextElementPosition > -1) && (NextEntryPos > -1))
409  // condition check added as precaution after SloughIECC error reported by James U
410  {
411  if((Track->TrackElementAt(170, NextElementPosition).Config[Track->GetNonPointsOppositeLinkPos(NextEntryPos)] == Signal) &&
412  (Track->TrackElementAt(171, NextElementPosition).Attribute == 0) && !StoppedWithoutPower)
413  {
414  FrontElementSpeedLimit = Track->TrackElementAt(172, LeadElement).SpeedLimit01; // use 01 for convenience, not used
415  FrontElementLength = Track->TrackElementAt(173, LeadElement).Length01; // use 01 for convenience, not used
416  EntrySpeed = 0;
417  ExitSpeedHalf = 0;
418  ExitSpeedFull = 0;
419  MaxExitSpeed = 0;
420  BrakeRate = 0;
423  if(!StoppedAtLocation) //if it is stopped at location then don't want StoppedAtSignal until departure time if still red then, & UpdateTrain takes care of thet
424  {
425  StoppedAtSignal = true;
427  // TrainController->LogActionError(39, HeadCode, "", SignalHold, Track->TrackElementAt(754, NextElementPosition).ElementID);
428  }
430  {
431  // set both StoppedAtLocation & StoppedAtSignal, so that 'pass red signal' is offered in popup menu rather than move
432  // forwards, but don't change the background colour so still shows as stopped at location
433  StoppedAtSignal = true;
434  }
435  }
436  else
437  {
438  StoppedAtSignal = false;
439  if(NextEntryPos > 1)
440  {
441  ElementLength = Track->TrackElementAt(174, NextElementPosition).Length23;
442  SpeedLimit = Track->TrackElementAt(175, NextElementPosition).SpeedLimit23;
443  }
444  else
445  {
446  ElementLength = Track->TrackElementAt(176, NextElementPosition).Length01;
447  SpeedLimit = Track->TrackElementAt(177, NextElementPosition).SpeedLimit01;
448  }
449  if(EntrySpeed > SpeedLimit)
450  {
451  EntrySpeed = SpeedLimit;
452  }
454  {
456  }
458  TDateTime TestTime = TrainController->TTClockTime; // test
459  AnsiString TimeString = Utilities->Format96HHMMSS(TestTime); // test
460  SetTrainMovementValues(2, NextElementPosition, NextEntryPos);
461  // NextElement is the element to be entered
462 
463  // Precautionary check - If need to brake EntrySpeed may be too high, so set it to the speed at which
464  // can achieve ExitSpeedFull at the half braking rate.
466  {
467  double TempEntrySpeed = sqrt((MaxExitSpeed * MaxExitSpeed) + (3.6 * 3.6 * MaxBrakeRate * ElementLength));
468  // half braking
469  if(TempEntrySpeed < EntrySpeed)
470  {
471  EntrySpeed = TempEntrySpeed;
472  SetTrainMovementValues(3, NextElementPosition, NextEntryPos);
473  }
474  }
475  }
476  }
478  {
479  throw Exception("Error, LeadElement Exit Connection is NotSet");
480  }
481  }
482  if(MidElement > -1) // will be -1 if start on continuation
483  {
484  Straddle = LeadMid;
488  {
489  for(int x = 0; x < 4; x++)
490  {
491  HeadCodePosition[x] = HeadCodeGrPtr[3 - x];
492  }
493  }
494  else
495  {
496  for(int x = 0; x < 4; x++)
497  {
499  }
500  }
501  if(TrainMode == Timetable)
502  {
504  }
505  else
506  {
508  }
510  // pick up background bitmaps [0] & [1] & plot HeadCodes [0] & [1]
511 
514 /* Move check to AddTrain, also, now that can start on bridges need to check that other train is on same track before refusing
515  if((Track->TrackElementAt(182, LeadElement).TrainIDOnElement > -1) || ((MidElement > -1) && (Track->TrackElementAt(183, MidElement).TrainIDOnElement > -1)))
516  {
517  ShowMessage("Can't place train " + HeadCode + "; another train already present at location");
518  Utilities->CallLogPop(651);
519  return false;
520  }
521 */
526  PlotTrainGraphic(8, 0, Display);
527  PlotTrainGraphic(9, 1, Display);
528 
531 
532  // pick up background bitmaps [2] & [3]
533 
536 
537  PlotElement[2] = MidElement;
539  PlotElement[3] = MidElement;
541  PlotTrainGraphic(10, 2, Display);
542  PlotTrainGraphic(11, 3, Display);
543  // Plotted = true; set in PlotTrainGraphic
544  }
545  Display->Update(); // resurrected when Update() dropped from PlotOutput etc
546  Utilities->CallLogPop(652);
547 }
548 
549 // ---------------------------------------------------------------------------
550 void TTrain::UnplotTrain(int Caller)
551 {
552  // Note: If trouble is experienced with the PlotAlternativeTrackRouteGraphic functions remove them & test for train on a bridge and if so call Clearand..
553  if(!Plotted)
554  {
555  return;
556  }
557  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",UnplotTrain," + HeadCode);
558 
559  if(Straddle == MidLag)
560  {
561  if(MidElement > -1)
562  {
567  // to force plot of locked route marker, needed once only for the element
568  }
569  if(LagElement > -1)
570  {
575  // to force plot of locked route marker, needed once only for the element
576  }
577  }
578  else if(Straddle == LeadMidLag)
579  {
580  if(LeadElement > -1)
581  {
584  // to force plot of locked route marker, needed once only for the element
585  }
586  if(MidElement > -1)
587  {
592  // to force plot of locked route marker, needed once only for the element
593  }
594  if(LagElement > -1)
595  {
598  // to force plot of locked route marker, needed once only for the element
599  }
600  }
601  else if(Straddle == LeadMid)
602  {
603  if(LeadElement > -1)
604  {
609  // to force plot of locked route marker, needed once only for the element
610  }
611  if(MidElement > -1)
612  {
617  // to force plot of locked route marker, needed once only for the element
618  }
619  }
620  if(LeadElement > -1)
621  {
623  }
624  if(MidElement > -1)
625  {
627  }
628  if(LagElement > -1)
629  {
631  }
632  Plotted = false;
634  Display->Update();
635  // without this the screen 'blinks' at next Clearand... prob forces a full repaint for some reason
636  // resurrected when Update() dropped from PlotOutput etc
637  Utilities->CallLogPop(653);
638 }
639 
640 // ----------------------------------------------------------------------------
641 
642 void TTrain::UpdateTrain(int Caller)
643 /*
644  Note: Some changes made since comments written
645 
646  Brief:
647  Enter with Straddle defining train position wrt Lag, Mid & Lead elements. Is only MidLag at this point
648  on first entry at a continuation (with no train plotted), in all other cases it is either LeadMid (when train fully
649  on Lead & Mid elements) or LeadMidLag (when train straddling 3 elements).
650  Thereafter on entry Straddle = LeadMidLag or LeadMid; LeadMid if train fully on Mid & Lead elements, and
651  LeadMidLag if on Lag, Mid and Lead elements (back on lag, front on Lead, & middle 2 segments on Mid).
652  If enter with Straddle = LeadMid, then train is in effect in the first half of the next element, and moves half onto it after
653  the half time point has been passed. The values for the next element were set when the train was last updated when Straddle became
654  LeadMid from LeadMidLag. After the half time point has been passed Straddle is
655  changed to MidLag within the function and all elements moved down one, old Mid becomes
656  the new lag, old Lead becomes the new Mid, and a new Lead is obtained. Then the new positions are plotted, and finally Straddle is
657  incremented to reflect the position the train now occupies.
658 
659  Detail:
660  Set TrainFailurePending if all conditions met
661  Check whether stopped at a non-red signal, and if so reset StoppedAtSignal so train can move.
662  Check whether buffers at immediate exit, either when first enter the function or later, and set StoppedAtBuffers if so
663  and return.
664  If Straddle == LeadMid then train fully on Lead and Mid, so ready for a major update:-
665  If there's a LagElement (there will be but include check for good practice - next
666  function depends on it) Check whether DerailPending set - set during last GetLeadElement if appropriate but only acted on here when
667  train fully on offending point - Derail set and DerailPanding reset, train background
668  colour changed (note that BackgroundColour is a property of the train itself) then return.
669  If no derail pending reset Lag and Mid elements to the old Mid and Lead values, reset Straddle to MidLag, then set
670  the new LeadElement, which will be the next connected element (obtain using GetLeadElement) or -1 if the current
671  LeadElement is an exit continuation. During GetLeadElement the element at LeadElement is checked and if a stop
672  signal is found StoppedAtSignal is set to true, otherwise StoppedAtSignal is set to false. Also Derail is set
673  if LeadElement is a fouled trailing point.
674  Now, the train is moved on by one segment. Firstly the last BackgroundElement is set to LagElement, then the last
675  segment of the LagElement is unplotted (if there is a LagElement - may be entering at a continuation), by
676  replotting the last background segment and checking whether the element is a bridge or crossover with the other
677  track in a route, in which case the route colour is replotted.
678  Then, if Straddle == LeadMidLag (train will move completely off the element during this function), and the train
679  track is in a route, then all the train elements are removed from the route unless it's an autosig route. Normally only the
680  LeadElement will be in a route for a moving train, but when originally placed all elements may be in the route so check them all.
681  Note also that there may be two routes at a given element position, but only one of them is the correct one, so this
682  is identified prior to the removal. Also the TrainIDs are reset because the train will be fully off this element at the end of
683  the function. If Straddle == LeadMidlag and the element being left is a ContinuationExit the the TrainGone flag is set so the
684  train can be deleted by the calling function, and the function returns.
685  If the element is a signal in the train movement direction, then it is reset to red (Attribute = 0) and is replotted
686  to show the red aspect. Finally if element is a signal in the other direction it is replotted as it was - need to
687  plot individually because could have any aspect, the background bitmap that was picked up earlier contains just the
688  basic red aspect.
689 
690  Now all the array values are updated, but the [0] values are as yet invalid, these have to be obtained explicitly from
691  the new LeadElement later. The headcode graphics are updated so that it reads correctly - left to right & top to bottom,
692  regardless of direction, and with the correct front code colour.
693 
694  The new front segment background bitmap is now picked up and the graphic offsets set, and the segments are plotted.
695  No more unplotting is needed as all but the last segment are overwritten by later segments, and the new front
696  segment is just plotted, though the background bitmap at that location has to be picked up. Just where they are
697  plotted depends on the Straddle value, [0] is always on Lead, [1] is on Lead if Straddle == LeadMidLag or Mid if
698  Straddle == MidLag; [2] is always on Mid, and [3] is on Mid if Straddle == LeadMidLag or Lag if Straddle == MidLag.
699  Also prior to plotting the lead segment a crash check is made, and if true the Crashed flag is set and the
700  TrainCrashedInto value also set to the current TrainID - this is so it too becomes crashed and hence stopped.
701 
702  The Crashed flag is now checked, and if set the front headcode colour is changed to the same as the rest of the code,
703  and the background colour changed. Then the train that is crashed into is also set to Crashed, and its colours
704  changed similarly. The function then returns.
705 
706  If Crashed is not set then Straddle is incremented and the function returns.
707 */
708 
709 {
710  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",UpdateTrain," + HeadCode);
711  UpdateCounter++;
712  // 100 counts = 5secs (used in splits to prevent too frequent length checks in front & rear splits)
713  if(UpdateCounter >= 100)
714  {
715  UpdateCounter = 0;
716  }
717  int RandRange = (TrainController->MTBFHours * 3600) / 53;
718 
719  // MTBFHours is in timetable clock hours, min value is 1 & max value is 10,000 (integer values on input)
720  // but double on use because it represents timetable clock time, so at 1/16 speed RandRange is * 16 (160,000 max) & at 16x speed its /16 (1/16 min)
721  // i.e MTBFHours is Input value/TTClockSpeed (conversion is done in InterfaceUnit)
722  if(int(TrainController->RandomFailureCounter) == (rand() % 1060))
723  // RandomFailureCounter value is fixed for a full cycle of train updates so this
724  // makes sure there's no bunching of failures as there is for a fixed comparison number
725  // or a small range of comparison numbers. True every 53 secs (real time) on average rand()
726  // gives a random number between 0 and 32767 (defined as RAND_MAX in stdlib.h)
727  {
728  if(!TrainFailed && !TrainOnContinuation(0) && (RandRange > 0) && (PowerAtRail > 1) && !((TrainMode == Timetable) && TimetableFinished)
729  && !Crashed && !Derailed && !((TrainMode == Signaller) && Stopped()))
730  // RandomFailureCounter resets to 0 every 53 secs, if RandRange is 0 then no failure rate is set - i.e. failure rate = 0
731  // don't fail if:
732  // (a) on a continuation (entering or leaving);
733  // (b) already failed;
734  // (c) power effectively zero (8000) min value for powered; 0.08 for 'no power';
735  // (d) train terminated;
736  // (e) crashed or derailed; or
737  // (f) under signaller control and stopped.
738  {
739  if((random(RandRange)) == 0)
740  // max value for RandRange is over 2x10^9
741  {
742  // here if failure due
743  TrainFailurePending = true;
744  // fail when PlotElements set to proper Lead & Mid Elements
745  }
746  }
747  }
748 /* dropped as it allows a train to stop on a half element - when reach (if Stopped()) at line 1310
749  if ((PowerAtRail < 1) && (EntrySpeed < 1)) // added at v2.4.0
750  {
751  StoppedWithoutPower = true;
752  }
753 */
754 // float TimeToExit; //added at v2.10.0 Removed these so original values retained - used when train on continuation
755 // THVShortPair ExitPair; //added at v2.10.0
756  int LockedVectorNumber;
757  Graphics::TBitmap *EXGraphicPtr = RailGraphics->bmTransparentBgnd;
758  // default values - these needed for route checker below
759  Graphics::TBitmap *EntryDirectionGraphicPtr = RailGraphics->bmTransparentBgnd;
760 
762  {
764  }
765  if(Crashed || Derailed)
766  {
768  {
769  PlotTrain(7, Display);
770  // replotted every cycle because of level crossing crashes, otherwise a flashing level crossing wipes out half of the train
771  Display->Update();
772  }
773  OpTimeToAct = 0.0;
774  // need to set this here as wouldn't be calculated otherwise as return from UpdateTrain
775  Utilities->CallLogPop(1017);
776  return; // no further action, user has to remove or work around
777  }
779  {
781  }
783  {
785  }
787  // introduced at v1.2.0, formerly 'TimeTimeLocArrived = false' was included
788  // in the next condition 'if(!Stopped() && !SPADFlag)' which led to repeated arrival messages if signaller control allowed a train
789  // to move & then stop again at the same station
790  {
791  TimeTimeLocArrived = false;
792  }
793  if(!Stopped() && !SPADFlag && !TrainFailed)
794  {
796  }
797  // set or release StoppedAtBuffers if fully on 2 elements depending on LeadElement
798  // Note that if LeadElement == Buffers train must be facing the buffer so no need to check orientation
799 /* old version where force a stop at buffers regardless of speed
800  if((Straddle == LeadMid) && (LeadElement > -1) && (Track->TrackElementAt(, LeadElement).TrackType == Buffers)) StoppedAtBuffers = true;
801  else StoppedAtBuffers = false;
802 */
803 
804  // new version where crash if run into buffers
805  if(!Crashed)
806  {
807  if((Straddle == LeadMid) && (LeadElement > -1) && (Track->TrackElementAt(602, LeadElement).TrackType == Buffers))
808  {
809  if(ExitSpeedFull > 1)
810  {
811  Crashed = true;
815  // SendMissedActionLogs(3, -1, ActionVectorEntryPtr);//-1 is a marker for send messages for all remaining entries, including Fer if present
816  // no need for missed action logs - will be sent when train removed
817  StoppedAtBuffers = false;
818  }
820  // stopped at location & stopped without power take precedence
821  {
822  StoppedAtBuffers = true;
823  }
824  else
825  {
826  StoppedAtBuffers = false;
827  }
828  }
829  else
830  {
831  StoppedAtBuffers = false;
832  }
833  }
834  else
835  {
836  StoppedAtBuffers = false;
837  }
838  // if crashed don't want stopped at buffers set
839 
840  // also crash if run into a level crossing that is changing or has barriers up
841  if(!Crashed)
842  {
843  if((Straddle == LeadMid) && (LeadElement > -1) && (ExitSpeedFull > 1))
844  {
845  int H = Track->TrackElementAt(873, LeadElement).HLoc;
846  int V = Track->TrackElementAt(874, LeadElement).VLoc;
847  if(Track->IsLCAtHV(40, H, V) && !Track->IsLCBarrierDownAtHV(2, H, V))
848  {
849  Crashed = true;
853  // no need for missed action logs - will be sent when train removed
854  }
855  }
856  }
858  {
860  }
861  // set or reset HoldAtLocationInTTMode (if true then actions are needed before train departs)
862  if((TrainMode == Timetable) && StoppedAtLocation && (ActionVectorEntryPtr->Command != "")) //if Command == "" then either TimeLoc or TimeTimeLoc
863  {
864  HoldAtLocationInTTMode = true;
865  }
866  else if(TrainMode == Timetable)
867  {
868  HoldAtLocationInTTMode = false;
869  }
870  // in Signaller mode HoldAtLocationInTTMode not changed
871 
872  // check if departure pending & set times unless already set
873  if(TrainMode == Timetable)
874  {
876  // && !StoppedAtBuffers) - drop this, set times whether or not at buffers
877  {
878  if(ActionVectorEntryPtr->DepartureTime > TDateTime(-1))
879  {
881  if(ReleaseTime <= LastActionTime + TDateTime(30.0 / 86400))
882  {
883  ReleaseTime = LastActionTime + TDateTime(30.0 / 86400);
884  }
885  TRSTime = ReleaseTime - TDateTime(10.0 / 86400);
886  DepartureTimeSet = true;
887  }
888  }
889  }
890  if(TrainController->OpTimeToActUpdateCounter == 0)// && TrainController->OpActionPanelVisible) removed last condition so always calc TimeToExit
891  {
892  OpTimeToAct = CalcTimeToAct(0, TimeToExit, ExitPair); // called after ReleaseTime set
893 // this->TimeToExit = TimeToExit; don't need these as values updated directly
894 // this->ExitPair = ExitPair;
895  // calculate every 1 sec (in real time, not timetable time) for all trains
896  }
897  // check if being held at location pending any actions & deal with them if time appropriate & >= 30s since LastActionTime
898  if(TrainMode == Timetable)
899  {
900  if((ActionVectorEntryPtr->Command != "Frh") && (ActionVectorEntryPtr->Command != "Frh-sh"))
901  {
902  RemainHereLogNotSent = true;
903  }
905  {
906  // ignore TimeLoc & TTLoc departures
907  // Action logs given in functions
909  LastActionTime + TDateTime(30.0 / 86400)))
910  {
911  if(ActionVectorEntryPtr->Command == "fsp")
912  {
913  // added for v1.3.2 because when add new train to TrainVector 'this' address likely invalidated, hence make no more changes to 'this' train. Next clock cycle will deal with any required changes
914  FrontTrainSplit(0);
915  if(TrainFailurePending) // ok, stopped so PlotElements set
916  {
917  TrainHasFailed(0);
918  }
919  Utilities->CallLogPop(2041);
920  return;
921  }
922  else if(ActionVectorEntryPtr->Command == "rsp")
923  {
924  // added for v1.3.2 because when add new train to TrainVector 'this' address likely invalidated, hence make no more changes to 'this' train. Next clock cycle will deal with any required changes
925  RearTrainSplit(0);
926  if(TrainFailurePending) // ok, stopped so PlotElements set
927  {
928  TrainHasFailed(1);
929  }
930  Utilities->CallLogPop(2042);
931  return;
932  }
933  else if(ActionVectorEntryPtr->Command == "Fjo")
934  {
935  FinishJoin(0);
936  }
937  else if(ActionVectorEntryPtr->Command == "jbo")
938  {
939  JoinedBy(0);
940  }
941  else if(ActionVectorEntryPtr->Command == "cdt")
942  {
944  }
945  else if(ActionVectorEntryPtr->Command == "Fns")
946  {
947  NewTrainService(0);
948  }
949  else if(ActionVectorEntryPtr->Command == "Frh")
950  {
951  RemainHere(0);
952  }
953  else if(ActionVectorEntryPtr->Command == "Fer")
954  {
955  TimetableFinished = true;
956  }
957  // other aspects of 'Fer' dealt with in TTrain::Update()
958  else if(ActionVectorEntryPtr->Command == "F-nshs")
959  {
961  }
962  else if(ActionVectorEntryPtr->Command == "Frh-sh")
963  {
965  }
966  else if(ActionVectorEntryPtr->Command == "Fns-sh")
967  {
969  }
970 /*
971  F-nshs (FNSShuttle) = Finish New Service (Shuttle) = finish, form new shuttle service in same direction, details =
972  shuttle headcode (no train creation)
973  Frh-sh (TimeCmdHeadCode) = Finish then restart as a shuttle using Snt-sh or Sns-sh, when all shuttle repeats done
974  remain here
975  Fns-sh (FSHNewService) = Finish then restart as a shuttle using Snt-sh or Sns-sh, when all shuttle repeats done
976  form new service via Sns-fsh using the NonRepeatingShuttleLinkHeadCode
977 */
978  }
979  }
980  else
981  {
983  {
985  }
986  }
987  }
988  if(TrainMode == Timetable)
989  {
990  if(StoppedAtBuffers)
991  {
992  // error if buffers (& element before it) not at a location, or if buffer location different to ActionVectorEntryPtr location
993  // if buffer location same as ActionVectorEntryPtr location & not Frh then error will be given for inability to depart
994  AnsiString BufferLocation = Track->TrackElementAt(604, LeadElement).ActiveTrackElementName;
995  if(BufferLocation == "")
996  {
998  }
999  AnsiString ExpectedLocation = ActionVectorEntryPtr->LocationName;
1000  if((BufferLocation == "") || (BufferLocation != ExpectedLocation))
1001  {
1005  {
1007  // SendMissedActionLogs(-1, ActionVectorEntryPtr);//-1 is a marker for send messages for all remaining entries, including Fer if present
1008  // Drop missed actions so user can still use sig mode to get back on track
1010  }
1011  if(TrainFailurePending) // ok, stopped so PlotElements set
1012  {
1014  TrainHasFailed(2);
1015  }
1016  Utilities->CallLogPop(1020);
1017  return;
1018  }
1019  else if((BufferLocation != "") && (BufferLocation == ExpectedLocation) && DepartureTimeSet && !StoppedAtLocation && (TrainController->TTClockTime >
1020  ReleaseTime))
1021  {
1024  {
1027  // SendMissedActionLogs(-1, ActionVectorEntryPtr);//-1 is a marker for send messages for all remaining entries, including Fer if present
1028  // Drop missed actions so user can still use sig mode to get back on track
1030  }
1031  if(TrainFailurePending) // ok, stopped so PlotElements set
1032  {
1034  TrainHasFailed(3);
1035  }
1036  Utilities->CallLogPop(1397);
1037  return;
1038  }
1039  }
1040  else
1041  {
1043  }
1044  }
1045  else
1046  {
1048  }
1049  if(TrainMode == Timetable)
1050  {
1052  {
1054  }
1056  {
1058  }
1059  }
1060  // Pick up element next to the train front (if exists) to check for calling-on, restart after a cleared signal, or
1061  // restart after stopped for train in front
1062  int NextElementPosition, NextEntryPos;
1063 
1064  if(LeadElement > -1) // if an exit continuation then not set
1065  {
1066  if((Track->TrackElementAt(186, LeadElement).TrackType != Points) || ((LeadEntryPos != 0) && (LeadEntryPos != 2)))
1067  {
1069  }
1070  else if((Track->TrackElementAt(187, LeadElement).TrackType == Points) && ((LeadEntryPos == 0) || (LeadEntryPos == 2)))
1071  {
1072  if(Track->TrackElementAt(188, LeadElement).Attribute == 0)
1073  {
1074  LeadExitPos = 1;
1075  }
1076  else
1077  {
1078  LeadExitPos = 3;
1079  }
1080  }
1081  NextElementPosition = Track->TrackElementAt(189, LeadElement).Conn[LeadExitPos];
1082  NextEntryPos = Track->TrackElementAt(190, LeadElement).ConnLinkPos[LeadExitPos];
1083  }
1084  else
1085  {
1086  NextElementPosition = -1;
1087  NextEntryPos = -1;
1088  }
1089  if((NextElementPosition > -1) && (NextEntryPos > -1))
1090  // may be buffers or continuation so need this check
1091  {
1092 /*
1093  Check whether calling-on conditions met:-
1094  a) approaching train has stopped at a signal but not at a location;
1095  b) if there is a facing train at the station, it is being held appropriately (must be awaiting a join (Fjo or jbo) or a
1096  change of direction (cdt), remaining here (Frh), or under signaller control);
1097  c) at least 1 platform available for the approaching train;
1098  d) points (if any) set for direct route into platform;
1099  e) approaching train is to stop at station;
1100  f) no more facing signals between train and platform;
1101  g) [dropped g]
1102  h) train in front preventing route being set far enough to release stop signal;
1103  i) train in front not exiting at continuation;
1104  j) signal must be within 4km of the stop platform;
1105  k) [dropped (k), now can set a reoute or part route into platform so can set points more easily];
1106  l) no existing route conflicts with the route into the platform; and
1107  m) not failed or without power (these added at v2.10.0)
1108  If all OK & route or part route not already set then set an unrestricted route into the station (just to the first platform), to prevent point changing or
1109  other route conflicts - if a partial route set than can still change points outside the route or have a route conflict if another route is set.
1110 */
1111  if(TrainMode == Timetable)
1112  {
1113  if(CallingOnAllowed(0)) //returns false if failed or no power (modified afer v2.9.2)
1114  {
1115  CallingOnFlag = true;
1116  PlotTrainWithNewBackgroundColour(1, clCallOnBackground, Display); // calling-on background
1117  }
1118  else
1119  {
1120  if(CallingOnFlag) //TrainHasFailed sets this flag to false (at v2.10.0)
1121  {
1122  if(!TrainFailed) //shouldn't be needed but include for safety at v2.10.0
1123  {
1125  }
1126  }
1127  CallingOnFlag = false;
1128  }
1129  }
1130  if(StoppedAtSignal && ((Track->TrackElementAt(191, NextElementPosition).Attribute > 0) || AllowedToPassRedSignal) && !TrainFailed && !StoppedAtLocation)
1131  {
1132  //'&& !StoppedAtLocation' added at v2.7.0 as if had been stopped at signal before tt control restored then background colour changed to normal when signal changed from red
1133  // reset PassRedSignal when reached half-way point in next element, if reset here then SetTrainMovementValues
1134  // sets StoppedAtSignal again & train doesn't move
1135  StoppedAtSignal = false;
1136  // need to recalculate exit times since old entry time expired. Straddle now at MidLag with front of train on MidElement
1137  // hence use MidElement for the calculation so same as would have been used if signal not red, when Straddle was
1138  // LeadMidLag and front of train was on LeadElement (after the current move)
1140  EntrySpeed = 0;
1142  FirstHalfMove = true;
1143  SetTrainMovementValues(4, NextElementPosition, NextEntryPos);
1144  // NextElement is the element to be entered
1145  }
1147  {
1148  if(ClearToNextSignal(0))
1149  {
1150  StoppedForTrainInFront = false;
1151  BeingCalledOn = false;
1152  EntrySpeed = 0;
1154  FirstHalfMove = true;
1155  SetTrainMovementValues(16, NextElementPosition, NextEntryPos);
1156  }
1157  else
1158  {
1159  if(TrainFailurePending) // ok, stopped so PlotElements set
1160  {
1161  TrainHasFailed(4);
1162  }
1163  Utilities->CallLogPop(1097);
1164  return;
1165  }
1166  }
1167  }
1168  if((Straddle == MidLag) && (LeadElement != -1))
1169  // later check only for Straddle == LeadMid, so need this check here for initial train start
1170  {
1172  }
1173 /* Logic below as follows: This check is made to allow a restart if had StoppedAtLocation or StoppedForTrainInFront or
1174  both but potentially able to restart (i.e. not at buffers, not crashed, not derailed, not held at location, departure
1175  time due, no train in front now & no other stop condition). Note that can be StoppedForTrainInFront when not at a
1176  location since this is set in SetTrainMovementValues whenever a train has zero EntrySpeed and there is a train in front,
1177  which could be when start as Snt.
1178  If StoppedForTrainInFront but not StoppedAtLocation then need to set TRSTime high so pink not plotted, and ReleaseTime
1179  low so can restart if appropriate. BeingCalledOn was set so that when train stopped at a station it wouldn't restart
1180  until the line was clear of trains up to the next signal. Hence check whether BeingCalledOn & if so set
1181  StoppedForTrainInFront, this ensures two things - that the restart check is carried out at each cycle and also that
1182  a restart won't happen until the line is clear to the next signal, regardless of whether or not the ReleaseTime has been
1183  reached.
1184  Then check if TRS time reached & change background to pink if so, & check if release time reached & if so change
1185  background to white and clear StoppedAtLocation. Then make check of station name, and recheck StoppedForTrainInFront,
1186  if it's set check if ClearToNextSignal and if so clear StoppedForTrainInFront & BeingCalledOn. If not ClearToNextSignal
1187  then return. If either not StoppedForTrainInFront or ClearToNextSignal then restart, calling SetTrainMovementValues &
1188  sending a message to the performancelog.
1189 */
1190 
1191  if(TrainMode == Timetable)
1192  {
1194  {
1195  if(BeingCalledOn)
1196  {
1197  StoppedForTrainInFront = true;
1198  }
1200  {
1202  }
1204  {
1205  // value updated at every scheduled departure & arrival
1207  AnsiString StationName;
1209  {
1211  }
1213  {
1215  }
1216  else
1217  {
1218  throw Exception("Error - Stopped at through station but neither lead nor mid elements have a name");
1219  }
1220  EntrySpeed = 0;
1224  FirstHalfMove = true;
1225  StoppedAtLocation = false;
1226 
1227  if((PowerAtRail < 1) && EntrySpeed < 1) // added at v2.4.0
1228  {
1229  StoppedWithoutPower = true;
1230  }
1231  if((NextElementPosition > -1) && (NextEntryPos > -1))
1232  // condition check added for SloughIECC error reported by James U
1233  {
1234  if((Track->TrackElementAt(720, NextElementPosition).Config[Track->GetNonPointsOppositeLinkPos(NextEntryPos)] == Signal) &&
1235  (Track->TrackElementAt(721, NextElementPosition).Attribute == 0))
1236  {
1237  StoppedAtSignal = true;
1238  if(!StoppedWithoutPower)
1239  // if stopped without power just keep existing background colour
1240  {
1242  // TrainController->LogActionError(40, HeadCode, "", SignalHold, Track->TrackElementAt(755, NextElementPosition).ElementID);
1243  }
1244  }
1245  }
1247  {
1248  TimeTimeLocArrived = false;
1249  LogAction(27, HeadCode, "", Depart, StationName, ActionVectorEntryPtr->DepartureTime, false);
1250  // no warning for TimeTimeLoc departure
1251  }
1252  else
1253  {
1255  }
1256  DepartureTimeSet = false;
1257  // no need to set LastActionTime for a departure
1258  //deal here with departure pointer change, increment if SkippedDeparture
1259  if(SkippedDeparture)
1260  {
1261  TrainController->SkippedTTEvents += TrainSkippedEvents; //TrainSkippedEvents is 1 less than PassNum
1263  TrainSkippedEvents = 0;
1264  SkippedDeparture = false;
1265  SkipPtrValue = 0;
1266  ActionsSkippedFlag = false;
1267  }
1268  else
1269  {
1271  }
1272  // advance pointer beyond departure action - (this line (& LogAction) used to be at the end - see
1273  // note
1274 /*
1275  Note: If train stops at station after call on with a TimeTimeLoc loaded, and before the normal stop point, then when
1276  SetTrainMovementValues called it assumes a stop at the stop point because the ActionVectorEntryPtr points to a name
1277  when NameInTimetableBeforeCDT is called and the stop positions are valid. So next element train movement is based on
1278  this calculation. However, when the departure time check is made (it is during this function when SetTrainMovementValues
1279  is called), the ActionVectorEntryPtr is advanced at the end past the departure location, so at the next element when
1280  SetTrainMovementValues is called again, all is normal, i.e. the train doesn't stop again at the location. But to cure
1281  the problem move the ActionVectorEntryPtr increment to before SetTrainMovementValues.
1282 */
1284  {
1285  StoppedAtBuffers = true;
1286  }
1287  else if(!StoppedWithoutPower)
1288  // if buffers or no power, don't set values
1289  {
1291  {
1292  SetTrainMovementValues(12, NextElementPosition, NextEntryPos);
1293  // NextElement is the element to be entered
1294  }
1295  else
1296  {
1298  // use LeadElement for an exit continuation
1299  }
1300  }
1301  }
1302  }
1303  }
1304  if(Straddle == LeadMidLag) //train on a half element
1305  {
1307  {
1308  Utilities->CallLogPop(654);
1309  return;
1310  }
1311  }
1312  else //train fully on 2 elements
1313  {
1315  {
1316  Utilities->CallLogPop(655);
1317  return;
1318  }
1319  }
1320  if((LeadElement > -1) && (MidElement > -1))
1321  {
1323  {
1324  // don't allow to stop if exiting at a continuation as causes problems if try to change direction
1325  // if entering at continuation & LeadElement is a continuation then MidElement will be -1
1326  //don't need to check for MidElement being continuation because popup menu won't show when exiting at continuation so SignallerStoppingFlag can't be set
1327  SignallerStoppingFlag = false;
1328  StepForwardFlag = false;
1329  }
1330  }
1331  if(Stopped())
1332  // this is what prevents another movement if the train is stopped
1333  {
1334  if(TrainFailurePending) // ok, stopped so PlotElements set
1335  {
1336  TrainHasFailed(5);
1337  }
1338  BrakeRate = 0;
1339  Utilities->CallLogPop(656);
1340  return;
1341  }
1342  // here when ready for next move
1343 
1344  //added at v2.10.0 to set SPADFlag if red signal immediately ahead (as it will be if in a locked route)
1345  //check if due to run past a red signal & if so set SPADFlag (SetTrainMovementValues & its SPAD check only called when arrive fully on 2 elements)
1346  if(Straddle == LeadMid) //fully on 2 elements
1347  {
1348  if(LeadElement > -1)
1349  {
1350  if(Track->TrackElementAt(1402, LeadElement).Conn[LeadExitPos] > -1)
1351  {
1353  if(TIF.TrackType == SignalPost)
1354  {
1355  int TIFEntryPos = Track->TrackElementAt(1405, LeadElement).ConnLinkPos[LeadExitPos];
1356  int TIFExitPos = 0;
1357  if(TIFEntryPos == 0)
1358  {
1359  TIFExitPos = 1;
1360  }
1361  if((TIF.Config[TIFExitPos] == Signal) && TIF.Attribute == 0 && (ExitSpeedHalf > 1) && !AllowedToPassRedSignal) //use ExitSpeedHalf as may have been stopped at signal so entryspeed is 0
1362  {
1363  SPADFlag = true; // user has to intervene to reset & restart after spad
1364  }
1365  }
1366  }
1367  }
1368  }
1369 
1370  // check for train in front & if so stop at next access (when train fully on element next to train)
1371  if((TrainMode == Signaller) && (Straddle == LeadMidLag))
1372  // SetTrainMovementValues brakes & stops signaller mode train for a train in front using local
1373  // variable TrainInFrontInSignallerModeFlag
1374  {
1375  if(LeadElement > -1)
1376  {
1377  int NextPos = Track->TrackElementAt(649, LeadElement).Conn[LeadExitPos];
1378  int NextEntryPos = Track->TrackElementAt(650, LeadElement).ConnLinkPos[LeadExitPos];
1379  if(Track->OtherTrainOnTrack(1, NextPos, NextEntryPos, TrainID))
1380  // true if another train on NextEntryPos track whether bridge or not
1381  {
1382  StoppedForTrainInFront = true;
1383  }
1384  else
1385  {
1386  StoppedForTrainInFront = false;
1387  }
1388  }
1389  }
1390  if((Straddle == LeadMid) && SPADFlag)
1391  // give message + plot background when ready to move half past the signal
1392  {
1393  if(NextElementPosition > -1)
1394  {
1395  if((Track->TrackElementAt(662, NextElementPosition).Config[Track->GetNonPointsOppositeLinkPos(NextEntryPos)] == Signal) &&
1396  (Track->TrackElementAt(663, NextElementPosition).Attribute == 0))
1397  {
1398  AnsiString LocID = AnsiString(Track->TrackElementAt(664, NextElementPosition).ElementID);
1400  // if goes past 2 signals then give message twice
1402  }
1403  }
1404  }
1405  if(Straddle == LeadMidLag)
1406  // During this function train moves fully onto 2 elements, Lead & Mid, so set next 2 moves from here for the element after Lead
1407  {
1408  // if SPADFlag set allow to keep moving until signal obscured before setting background colour, & stop only when ExitSpeedFull is 0
1409  if(SPADFlag)
1410  {
1411  if(ExitSpeedFull == 0)
1412  {
1413  StoppedAfterSPAD = true;
1414  // but don't want to stop until have moved fully onto element, hence stop test is before this check
1415  }
1416  }
1418  {
1419  if(ExitSpeedFull == 0)
1420  {
1421  // only reach here when will stop on LeadMid, because SetTrainMovementValues called after this (i.e. ExitSpeedFull becomes 0 if not 0 now
1422  // after this test), and Straddle == LeadMidLag so not accessed at the half-move point, hence only reached at the full move
1423  // point when the speed is 0. So, colour change won't occur until fully stopped (early in UpdateTrain()), and the log message
1424  // is sent at the right time and once only.
1425  SignallerStopped = true;
1426  // but don't want to stop until have moved fully onto element, hence stop test is before this check
1427  StepForwardFlag = false;
1428  SignallerStoppingFlag = false;
1429  TTrackElement TE;
1430  AnsiString Loc = "";
1431  bool LocNamed = false;
1432  if(LeadElement > -1)
1433  {
1434  TE = Track->TrackElementAt(782, LeadElement);
1435  if(TE.ActiveTrackElementName != "")
1436  {
1437  Loc = TE.ActiveTrackElementName;
1438  LocNamed = true;
1439  }
1440  else
1441  {
1442  Loc = "track element " + TE.ElementID;
1443  }
1444  }
1445  if((MidElement > -1) && !LocNamed)
1446  {
1447  TE = Track->TrackElementAt(783, MidElement);
1448  if(TE.ActiveTrackElementName != "")
1449  {
1450  Loc = TE.ActiveTrackElementName;
1451  LocNamed = true;
1452  }
1453  else if(Loc == "")
1454  {
1455  Loc = "track element " + TE.ElementID;
1456  }
1457  }
1458  if(Loc == "")
1459  {
1460  Loc = "outside railway";
1461  // must have stopped after left at a continuation (because both lead & mid == -1)
1462  }
1463  else
1464  {
1465  Loc = "at " + Loc;
1466  }
1467  LogAction(30, HeadCode, "", SignallerStop, Loc, TrainController->TTClockTime, false); // false for warning
1468  }
1469  }
1470  if(LeadElement > -1) // if an exit continuation then not set
1471  {
1472  if((Track->TrackElementAt(202, LeadElement).TrackType != Points) || ((LeadEntryPos != 0) && (LeadEntryPos != 2)))
1473  {
1475  }
1476  else if((Track->TrackElementAt(203, LeadElement).TrackType == Points) && ((LeadEntryPos == 0) || (LeadEntryPos == 2)))
1477  {
1478  if(Track->TrackElementAt(204, LeadElement).Attribute == 0)
1479  {
1480  LeadExitPos = 1;
1481  }
1482  else
1483  {
1484  LeadExitPos = 3;
1485  }
1486  }
1487  NextElementPosition = Track->TrackElementAt(205, LeadElement).Conn[LeadExitPos];
1488  NextEntryPos = Track->TrackElementAt(206, LeadElement).ConnLinkPos[LeadExitPos];
1489  }
1490  else
1491  {
1492  NextElementPosition = -1;
1493  NextEntryPos = -1;
1494  }
1497  FirstHalfMove = true; //will be when finished the move onto 2 elements during this function
1498 
1499  if((PowerAtRail < 1) && EntrySpeed < 1) // added at v2.4.0
1500  {
1501  StoppedWithoutPower = true;
1502  }
1503  if((NextElementPosition > -1) && (NextEntryPos > -1) && !SPADFlag)
1504  // may be buffers or continuation. SPADFlag added at v2.1.0
1505  // so don't override the SPAD colour & don't set StoppedAtSignal
1506  {
1507  if((Track->TrackElementAt(207, NextElementPosition).Config[Track->GetNonPointsOppositeLinkPos(NextEntryPos)] == Signal) &&
1508  (Track->TrackElementAt(208, NextElementPosition).Attribute == 0) && (ExitSpeedFull < 1) && !StoppedAtLocation)
1509  {
1510  StoppedAtSignal = true;
1511  if(!StoppedWithoutPower)
1512  // leave background as is if no power, but set StoppedAtSignal
1513  {
1515  }
1516  // TrainController->LogActionError(41, HeadCode, "", SignalHold, Track->TrackElementAt(756, NextElementPosition).ElementID);
1517  }
1518  }
1519  if(!Stopped())
1520  {
1521  if((NextElementPosition > -1) && (NextEntryPos > -1))
1522  // may be buffers or continuation (skip SetTrainMovementValues if buffers, if
1523  // a stop element that isn't buffers - e.g. station, then will skip the calcs
1524  // during SetTrainMovementValues to avoid trying to divide by zero - see that
1525  // function for fuller explanation
1526  {
1527  SetTrainMovementValues(8, NextElementPosition, NextEntryPos);
1528  // NextElement is the element to be entered
1529  }
1530  // follow the continuation exits:-
1531  else if((LeadElement > -1) && (Track->TrackElementAt(209, LeadElement).TrackType == Continuation))
1532  {
1534  // Use LeadElement for calcs if lead is a continuation
1535  }
1536  else if((MidElement > -1) && (Track->TrackElementAt(210, MidElement).TrackType == Continuation))
1537  {
1539  // Use MidElement for calcs if mid is a continuation
1540  }
1541  else if((LagElement > -1) && (Track->TrackElementAt(211, LagElement).TrackType == Continuation))
1542  {
1544  // Use LagElement for calcs if lag is a continuation
1545  }
1546  }
1547  // remove route elements if not autosigs - this section moved from below, was under LagElement > -1 condition but needs to cover LagElement == -1
1548  if(AllRoutes->GetRouteTypeAndGraphics(2, LeadElement, LeadEntryPos, EXGraphicPtr, EntryDirectionGraphicPtr) == TAllRoutes::NotAutoSigsRoute)
1549  // Trains may not be in a route
1550  // Since Straddle = LeadMidLag at this point the train is going to move fully off the existing Lag & fully onto existing Lead element during this function
1551  {
1552  // NB if LeadElement == -1 then the above test returns false
1553  int TempH = Track->TrackElementAt(213, LeadElement).HLoc;
1554  int TempV = Track->TrackElementAt(214, LeadElement).VLoc;
1555  int TempELink = Track->TrackElementAt(215, LeadElement).Link[LeadEntryPos];
1556  TAllRoutes::TRouteElementPair FirstPair, SecondPair;
1557  FirstPair = AllRoutes->GetRouteElementDataFromRoute2MultiMap(10, TempH, TempV, SecondPair);
1558  if((FirstPair.first > -1) && (AllRoutes->GetFixedRouteAt(143, FirstPair.first).GetFixedPrefDirElementAt(153,
1559  FirstPair.second).GetELink() == TempELink))
1560  {
1561  AllRoutes->RemoveRouteElement(10, TempH, TempV, TempELink);
1562  }
1563  else if((SecondPair.first > -1) && (AllRoutes->GetFixedRouteAt(144, SecondPair.first).GetFixedPrefDirElementAt(154,
1564  SecondPair.second).GetELink() == TempELink))
1565  {
1566  AllRoutes->RemoveRouteElement(11, TempH, TempV, TempELink);
1567  }
1568  }
1569  if(AllRoutes->GetRouteTypeAndGraphics(3, MidElement, MidEntryPos, EXGraphicPtr, EntryDirectionGraphicPtr) == TAllRoutes::NotAutoSigsRoute)
1570  // Trains may not be in a route
1571  {
1572  int TempH = Track->TrackElementAt(216, MidElement).HLoc;
1573  int TempV = Track->TrackElementAt(217, MidElement).VLoc;
1574  int TempELink = Track->TrackElementAt(218, MidElement).Link[MidEntryPos];
1575  TAllRoutes::TRouteElementPair FirstPair, SecondPair;
1576  FirstPair = AllRoutes->GetRouteElementDataFromRoute2MultiMap(11, TempH, TempV, SecondPair);
1577  if((FirstPair.first > -1) && (AllRoutes->GetFixedRouteAt(145, FirstPair.first).GetFixedPrefDirElementAt(155,
1578  FirstPair.second).GetELink() == TempELink))
1579  {
1580  AllRoutes->RemoveRouteElement(12, TempH, TempV, TempELink);
1581  }
1582  else if((SecondPair.first > -1) && (AllRoutes->GetFixedRouteAt(146, SecondPair.first).GetFixedPrefDirElementAt(156,
1583  SecondPair.second).GetELink() == TempELink))
1584  {
1585  AllRoutes->RemoveRouteElement(13, TempH, TempV, TempELink);
1586  }
1587  }
1588  if(AllRoutes->GetRouteTypeAndGraphics(4, LagElement, LagEntryPos, EXGraphicPtr, EntryDirectionGraphicPtr) == TAllRoutes::NotAutoSigsRoute)
1589  // Trains may not be in a route
1590  {
1591  int TempH = Track->TrackElementAt(219, LagElement).HLoc;
1592  int TempV = Track->TrackElementAt(220, LagElement).VLoc;
1593  int TempELink = Track->TrackElementAt(221, LagElement).Link[LagEntryPos];
1594  TAllRoutes::TRouteElementPair FirstPair, SecondPair;
1595  FirstPair = AllRoutes->GetRouteElementDataFromRoute2MultiMap(12, TempH, TempV, SecondPair);
1596  if((FirstPair.first > -1) && (AllRoutes->GetFixedRouteAt(147, FirstPair.first).GetFixedPrefDirElementAt(157,
1597  FirstPair.second).GetELink() == TempELink))
1598  {
1599  AllRoutes->RemoveRouteElement(14, TempH, TempV, TempELink);
1600  }
1601  else if((SecondPair.first > -1) && (AllRoutes->GetFixedRouteAt(148, SecondPair.first).GetFixedPrefDirElementAt(158,
1602  SecondPair.second).GetELink() == TempELink))
1603  {
1604  AllRoutes->RemoveRouteElement(15, TempH, TempV, TempELink);
1605  }
1606  AllRoutes->CheckMapAndRoutes(8); // test
1607  }
1608  if(LagElement > -1)
1609  // not entering at a continuation so can deal with train leaving the lag element
1610  {
1612  // amended below so route elements removed for the complete train (for NotAutoSigsRoutes), so train never standing on a route once it
1613  // starts moving, covers for eliminating route when train reaches buffers, and prevents odd route segments when route extended while
1614  // straddling 3 elements (formerly the last segment was replotted as a route & stayed plotted
1615 
1616  TPrefDirElement PrefDirElement;
1617  // plot locked route marker for any element if appropriate (i.e. if a locked AutoSigs route) but only when train leaves element completely
1618  // as this is a 16x16 graphic
1620  {
1622  RailGraphics->LockedRouteCancelPtr[PrefDirElement.GetELink()]);
1623  }
1624  if(ContinuationExit(2, LagElement, LagExitPos)) // true if Element is a continuation and Exitpos is the continuation end
1625  {
1626  int RouteNumber;
1627  TrainGone = true;
1628  // flag to indicate train to be deleted - outside this function
1630  {
1631  TTrainController::TContinuationAutoSigEntry ContinuationAutoSigEntry;
1632  ContinuationAutoSigEntry.RouteNumber = RouteNumber;
1633  // calc distance from & inc last signal to exit
1634  int LastElement = LagElement, LastExitPos = LagExitPos, CumDistance = 0;
1635  int NewLastElement = 0, NewLastExitPos = 0;
1636  // need above because can't change LastElement & LastExitPos until both new values obtained
1637  // while((Track->TrackElementAt(684, LastElement).Config[LastExitPos] != Signal) && (CumDistance < 1200)) as was
1638  while((Track->TrackElementAt(913, LastElement).Config[LastExitPos] != Signal) && (CumDistance < 1200) && (Track->TrackElementAt(897,
1639  LastElement).TrackType != Points))
1640  // extra condition above added because of Moric1998's error (see email of 24/03/2016), where had an autosigs route across points, and another continuation on track not occupied by route so
1641  // failed when found a new element = -1 when tried to cross the continuation. Note this routine can only deal with non points as it uses GetNonPointsOppositeLinkPos
1642  // leave CumDistance as it was in these circumstances.
1643  {
1644  if(LastExitPos < 2)
1645  {
1646  CumDistance += Track->TrackElementAt(685, LastElement).Length01;
1647  }
1648  else
1649  {
1650  CumDistance += Track->TrackElementAt(686, LastElement).Length23;
1651  }
1652  NewLastElement = Track->TrackElementAt(687, LastElement).Conn[Track->GetNonPointsOppositeLinkPos(LastExitPos)];
1653  if(NewLastElement == -1)
1654  // this will catch buffers or any other connection failure
1655  {
1656  throw Exception("Error, Connection = -1 in Continuation loop in UpdateTrain");
1657  }
1658  NewLastExitPos = Track->TrackElementAt(688, LastElement).ConnLinkPos[Track->GetNonPointsOppositeLinkPos(LastExitPos)];
1659  if(NewLastExitPos == -1)
1660  {
1661  throw Exception("Error, ConnLinkPos = -1 in Continuation loop in UpdateTrain");
1662  }
1663  LastElement = NewLastElement;
1664  LastExitPos = NewLastExitPos;
1665  }
1666  // if at signal add this in too
1667  if(CumDistance < 1200)
1668  {
1669  CumDistance += Track->TrackElementAt(689, LastElement).Length01; // only need 01 for signal
1670  }
1671  // now have distance including the signal, if >=1200m use 100m (for a signal immediately after the continuation)
1672  // else use 1200m - CumDistance
1673  int FirstDistance = 0;
1674  if(CumDistance >= 1200)
1675  {
1676  FirstDistance = 100;
1677  }
1678  else
1679  {
1680  FirstDistance = 1200 - CumDistance;
1681  }
1682  if(FirstDistance < 100)
1683  {
1684  FirstDistance = 100; // don't allow < 100
1685  }
1686  // can now calc the time delays in seconds - FirstDelay, SecondDelay & ThirdDelay, these are doubles
1687  // BUT - first check whether ExitSpeedFull is very low (Mark had divide by zero error with zero exit speed using v2.4.0)
1688  if(ExitSpeedFull > 20.0)
1689  {
1690  ContinuationAutoSigEntry.FirstDelay = 3.6 * double(FirstDistance) / ExitSpeedFull;
1691  // speed in km/h & distance in m so mult by 3.6 to bring to secs
1692  ContinuationAutoSigEntry.SecondDelay = ContinuationAutoSigEntry.FirstDelay + 4320.0 / ExitSpeedFull;
1693  // 4320.0 = 3.6 * 1200, .0 to make it a double
1694  ContinuationAutoSigEntry.ThirdDelay = ContinuationAutoSigEntry.SecondDelay + 4320.0 / ExitSpeedFull;
1695  }
1696  else
1697  {
1698  ContinuationAutoSigEntry.FirstDelay = 60.0; // 60 secs
1699  ContinuationAutoSigEntry.SecondDelay = 120.0;
1700  ContinuationAutoSigEntry.ThirdDelay = 180.0;
1701  }
1702  ContinuationAutoSigEntry.AccessNumber = 0;
1703  ContinuationAutoSigEntry.PassoutTime = TrainController->TTClockTime;
1705  {
1707  for(VectorIT = TrainController->ContinuationAutoSigVector.begin(); VectorIT != TrainController->ContinuationAutoSigVector.end();
1708  VectorIT++)
1709  {
1710  if(VectorIT->RouteNumber == RouteNumber)
1711  {
1712  // another train has passed out of same route so erase earlier entry
1713  TrainController->ContinuationAutoSigVector.erase(VectorIT);
1714  break;
1715  }
1716  }
1717  }
1718  TrainController->ContinuationAutoSigVector.push_back(ContinuationAutoSigEntry);
1719  }
1721  // need to plot this as returning early so will miss the later plot (not a bridge so don't need PlotAlternativeTrackRouteGraphic)
1722  Display->Update();
1723  // need to keep this since Update() not called for PlotSmallOutput as too slow
1724  Utilities->CallLogPop(659);
1725  return;
1726  }
1727  // above covers for exiting at continuation, need XLinkPos check to exclude entering at a continuation
1728  if(LeadElement > -1)
1729  {
1731  // changed to lead so reset early
1732  {
1733  Track->TrackElementAt(225, LeadElement).Attribute = 0; // red
1735  // don't plot if zoomed out
1736  if(!Display->ZoomOutFlag)
1737  {
1739  }
1740  // covers signal resetting in same direction
1741  }
1742  }
1744  {
1745  if(AllRoutes->GetRouteTypeAndGraphics(5, LagElement, LagEntryPos, EXGraphicPtr, EntryDirectionGraphicPtr) == TAllRoutes::AutoSigsRoute)
1746  {
1747  Display->PlotOutput(23, Track->TrackElementAt(227, LagElement).HLoc * 16, Track->TrackElementAt(228, LagElement).VLoc * 16, EXGraphicPtr);
1748  Display->PlotOutput(24, Track->TrackElementAt(229, LagElement).HLoc * 16, Track->TrackElementAt(230, LagElement).VLoc * 16, EntryDirectionGraphicPtr);
1749  TPrefDirElement PrefDirElement;
1750  // plot locked route marker for same side signal if appropriate (may be covered above but leave in), but only when train leaves element completely as this is a 16x16 graphic
1752  {
1754  RailGraphics->LockedRouteCancelPtr[PrefDirElement.GetELink()]);
1755  }
1757  LockedVectorNumber)))
1758  {
1760  }
1761  }
1762  }
1763  else if((LeadElement > -1) && (Track->TrackElementAt(233, LeadElement).TrackType == SignalPost))
1764  {
1765  Track->TrackElementAt(234, LeadElement).Attribute = 0; // red
1767  // don't plot if zoomed out
1768  if(!Display->ZoomOutFlag)
1769  {
1771  }
1772  // covers signal passed in opposite direction - replot as red, regardless of what it was before, though should already have been red
1773  }
1775  {
1776  if(AllRoutes->GetRouteTypeAndGraphics(6, LagElement, LagEntryPos, EXGraphicPtr, EntryDirectionGraphicPtr) == TAllRoutes::AutoSigsRoute)
1777  {
1778  Display->PlotOutput(26, Track->TrackElementAt(236, LagElement).HLoc * 16, Track->TrackElementAt(237, LagElement).VLoc * 16, EXGraphicPtr);
1779  Display->PlotOutput(27, Track->TrackElementAt(238, LagElement).HLoc * 16, Track->TrackElementAt(239, LagElement).VLoc * 16, EntryDirectionGraphicPtr);
1780  // below added at v1.3.0 to reset signals if back out of an autosigs route under signaller control after changing direction, when new LeadElement not on route (if it had
1781  // been the route would have been ForceCancelled). Note that the signal is not facing the direction of travel else would have entered
1782  // "if(Track->TrackElementAt(, LagElement).Config[LagExitPos] == Signal)" above and wouldn't be here
1783  int RouteNumber;
1785  // already know it's an autosigsroute, this is just to get the RouteNumber
1786  // addition below at v1.3.2 - found that a signal that had reached double yellow in ContinuationAutoSigs was reset to red when a following train's lag element
1787  // moved off a signal in the normal course of events. It was caused when a train backed out of an autosigs route under signaller control after changing
1788  // direction (see DevHistory.txt). Hence check that the train is in signaller mode and that the train's lead element isn't on the same route before calling SetRouteSignals.
1789  int RouteNumber2;
1791  // already know it's an autosigsroute, this is just to get the RouteNumber
1792  if((TrainMode == Signaller) && (RouteNumber2 != RouteNumber))
1793  // note that if not in a route (as likely) then RouteNumber2 set to -1 )
1794  {
1795  AllRoutes->GetFixedRouteAt(217, RouteNumber).SetRouteSignals(10);
1796  // this was in the 1.3.0 addition but without the condition
1797  }
1798  // end of 1.3.2 addition
1799  // end of 1.3.0.addition
1800  }
1801  TPrefDirElement PrefDirElement;
1802  // plot locked route marker for opp side signal if appropriate (may be covered above but leave in), but only when train leaves element completely as this is a 16x16 graphic (OK - Straddle == LeadMidLag)
1804  {
1806  RailGraphics->LockedRouteCancelPtr[PrefDirElement.GetELink()]);
1807  }
1808  }
1809  }
1810  }
1811  // straddle ONLY changed here, check if 'LeadMid' first & if so ready for updating Elements
1812  if(Straddle == LeadMid)
1813  {
1814  AllowedToPassRedSignal = false;
1815  // if had been allowed to pass then at this point it will move half onto signal so can be reset
1816  // if(LagElement > -1) ResetTrainElementID(LagElement, LagEntryPos);//train fully off old LagElement so can clear TrainOnElement flags - no, reset at earlier call when lag moves off element
1817  if(DerailPending)
1818  // set during last GetLeadElement, but only act on it when train fully on offending point
1819  // i.e. next time Straddle reaches LeadMid
1820  {
1821  Derailed = true;
1822  DerailPending = false;
1826  Utilities->CallLogPop(657);
1827  return;
1828  }
1835  Straddle = MidLag;
1836  // train now fully on the updated Lag & Mid, the front segment is going to move onto the new
1837  // LeadElement during this function (note that if stopped at signal then won't get this far)
1838  if(LeadElement > -1)
1839  {
1841  // i.e an exit continuation only
1842  // if don't exclude entry continuations then can't progress past it
1843  {
1844  LeadElement = -1;
1845  }
1846  else
1847  {
1848  GetLeadElement(0);
1849  // sets or resets DerailPending & StoppedAtSignal, and sets LeadElement values
1851  if(Stopped())
1852  {
1853  if(TrainFailurePending) // ok, stopped so PlotElements set
1854  {
1855  TrainHasFailed(6);
1856  }
1857  Utilities->CallLogPop(658);
1858  return; // i.e. don't move forward one step if next element is a red signal
1859  }
1860  }
1861  }
1862  }
1863  if(LagElement > -1)
1864  {
1865  // below are the actions required at both half moves for LagElement > -1
1867 
1868  // if was in locked route but has timed out when train leaves then plot the normal track graphic over the route graphic that is
1869  // still in BackgroundGraphic[3], if wasn't in a route then will just replot the same BackgroundGraphic
1870  // need to do this for each half element
1871 
1872  TPrefDirElement PrefDirElement;
1873  if(!(AllRoutes->IsElementInLockedRouteGetPrefDirElementGetLockedVectorNumber(7, LagElement, LagExitPos, PrefDirElement, LockedVectorNumber)))
1874  {
1875  int RouteNumber; // holder for call below - not used
1877  {
1878  if(Utilities->clTransparent == TColor(0xFFFFFF))
1879  // change to black for a white background
1880  {
1882  // only applies for AutoSigs Route in case was locked & timed out
1883  }
1884  else
1885  // change to white for a dark background
1886  {
1888  // only applies for AutoSigs Route in case was locked & timed out
1889  }
1891  }
1892  }
1894  // above in case train just moving off a bridge & either alternative track in a route - need to keep its route colour,
1895  // or a train on the opposite track - needs to be replotted
1896  }
1897  // update all array values
1898  HOffset[3] = HOffset[2];
1899  HOffset[2] = HOffset[1];
1900  HOffset[1] = HOffset[0];
1901  VOffset[3] = VOffset[2];
1902  VOffset[2] = VOffset[1];
1903  VOffset[1] = VOffset[0];
1904  Graphics::TBitmap *TempPtr = BackgroundPtr[3];
1905 
1906  BackgroundPtr[3] = BackgroundPtr[2];
1907  BackgroundPtr[2] = BackgroundPtr[1];
1908  BackgroundPtr[1] = BackgroundPtr[0];
1909  BackgroundPtr[0] = TempPtr;
1910 
1911  // update headcode graphics depending on Lead entry value
1912  if(LeadElement > -1) // if Lead is -1 then stays as is
1913  {
1915  {
1916  for(int x = 0; x < 4; x++)
1917  {
1918  HeadCodePosition[x] = HeadCodeGrPtr[3 - x];
1919  }
1920  }
1921  else
1922  {
1923  for(int x = 0; x < 4; x++)
1924  {
1926  }
1927  }
1928  }
1929  if(TrainMode == Timetable)
1930  {
1932  }
1933  else
1934  {
1936  }
1938 
1939  // plot new seg [0] on Lead & [2] on Mid ([2] always on Mid)
1940  if(LeadElement > -1)
1941  {
1942  if(Straddle == MidLag)
1943  // just about to move half onto the new lead element
1944  {
1946  // pick up new background bitmap [0]
1948  int LeadElementTrainID = Track->TrackElementAt(244, LeadElement).TrainIDOnElement;
1949  if((LeadElementTrainID > -1) && (LeadElementTrainID != TrainID))
1950  // check if own ID for entry at continuation, else crashes into itself!
1951  {
1952  // OK if crossing on a bridge
1953  int OtherTrainEntryPos = TrainController->EntryPos(0, LeadElementTrainID, LeadElement);
1954  if(OtherTrainEntryPos == -1)
1955  {
1956  throw Exception("Error - OtherTrainEntryPos not set");
1957  }
1958  if((Track->TrackElementAt(246, LeadElement).TrackType != Bridge) || (LeadEntryPos == OtherTrainEntryPos) ||
1959  // LeadEntryPos for rear end crashes
1960  (LeadExitPos == OtherTrainEntryPos))
1961  // LeadExitPos for head-on crashes
1962  {
1964  Crashed = true; // only set if Straddle = MidLag
1965  CallingOnFlag = false;
1966  // in case was set, need to disable call on if call on button had been pressed
1967  }
1968  }
1969  else if(MidElement > -1) // will be -1 for continuation entries
1970  {
1971  // check if about to move onto a crossing diagonal that is occupied by another train, and if so crash
1972  int MidExitLinkNum = Track->TrackElementAt(889, MidElement).Link[MidExitPos];
1973  int MidHLoc = Track->TrackElementAt(890, MidElement).HLoc;
1974  int MidVLoc = Track->TrackElementAt(891, MidElement).VLoc;
1975  int OtherTrainID = -1;
1976  if((MidExitLinkNum == 1) || (MidExitLinkNum == 3) || (MidExitLinkNum == 7) || (MidExitLinkNum == 9))
1977  {
1978  if(Track->DiagonalFouledByTrain(0, MidHLoc, MidVLoc, MidExitLinkNum, OtherTrainID))
1979  {
1980  TrainCrashedInto = OtherTrainID;
1981  Crashed = true; // only set if Straddle = MidLag
1982  CallingOnFlag = false;
1983  // in case was set, need to disable call on if call on button had been pressed
1984  }
1985  }
1986  }
1987  }
1988  else
1989  {
1991  // pick up new background bitmap [0]
1993  }
1994  PlotElement[0] = LeadElement;
1996  PlotTrainGraphic(12, 0, Display);
1997  }
1998  if(MidElement > -1)
1999  {
2000  PlotElement[2] = MidElement;
2002  PlotTrainGraphic(1, 2, Display);
2003  }
2004  // plot the new positions for [1] & [3] graphics - [1] on Mid if Straddle = MidLag, on Lead if Straddle = LeadMidLag
2005  // [3] on Lag if Straddle = MidLag, on Mid if Straddle = LeadMidLag
2006  if(Straddle == MidLag)
2007  {
2008  if(MidElement > -1)
2009  {
2010  PlotElement[1] = MidElement;
2012  PlotTrainGraphic(2, 1, Display);
2013  }
2014  if(LagElement > -1)
2015  {
2016  PlotElement[3] = LagElement;
2018  PlotTrainGraphic(3, 3, Display);
2019  }
2020  }
2021  else // Straddle == LeadMidLag
2022  {
2023  if(LeadElement > -1)
2024  {
2025  PlotElement[1] = LeadElement;
2027  PlotTrainGraphic(4, 1, Display);
2028  }
2029  if(MidElement > -1)
2030  {
2031  PlotElement[3] = MidElement;
2033  PlotTrainGraphic(5, 3, Display);
2034  }
2035  }
2036  if(Crashed)
2037  // only reach here if crash into another train, if crash into buffers or an LC then return earlier at the if(Stopped()) test
2038  {
2043  // in case was set, need to disable call on if call on button had been pressed
2050  Straddle = LeadMidLag;
2051  // was MidLag but plotted as LeadMidLag so change Straddle accordingly
2052  Display->Update();
2053  // resurrected when Update() dropped from PlotOutput etc
2054  Utilities->CallLogPop(660);
2055  return;
2056  }
2057  // deal here with station stops & pass times after all replotting done but before Straddle changed
2058  if(TrainMode == Timetable)
2059  {
2060  if(Straddle == LeadMidLag)
2061  {
2062  if((LeadElement > -1) && (MidElement > -1) && !TimetableFinished)
2063  {
2064  // NameInTimetableBeforeCDT returns the number by which the train ActionVectorEntryPtr needs to be incremented
2065  // to point to the location arrival entry - before a change of direction
2066  AnsiString LocName = Track->TrackElementAt(249, LeadElement).ActiveTrackElementName;
2067  bool StopRequired = false;
2068  int TTVPos = NameInTimetableBeforeCDT(1, LocName, StopRequired);
2069  if(TTVPos > -1) // -1 if can't find it or if name is ""
2070  {
2071  // check if at buffers (no, dropped buffer check to allow to crash into buffers) or a through station stop,
2072  // or a station where next element contains a train or a stop signal, if so
2073  // stop now, note that for 2nd check, if next element is a bridge then will have stopped by now so no need
2074  // to test the actual track the train is on since it can't be a platform
2075  TTrackElement LeadTrackElement = Track->TrackElementAt(258, LeadElement);
2076  TTrackElement NextTrackElement; // default for now
2077  bool TrainAtStopLinkPos1 = (LeadTrackElement.StationEntryStopLinkPos1 == LeadEntryPos);
2078  bool TrainAtStopLinkPos2 = (LeadTrackElement.StationEntryStopLinkPos2 == LeadEntryPos);
2079  bool ForwardConnection = (LeadTrackElement.Conn[LeadExitPos] > -1);
2080  int NextElementEntryPos = -1;
2081  int NextElementExitPos = -1;
2082  bool TrainOnNextElement = false;
2083  bool StopSignalAtNextElement = false;
2084  if(ForwardConnection)
2085  // if no forward connection can't derive anything from it without errors
2086  {
2087  NextTrackElement = Track->TrackElementAt(262, LeadTrackElement.Conn[LeadExitPos]);
2088  NextElementEntryPos = LeadTrackElement.ConnLinkPos[LeadExitPos];
2089  NextElementExitPos = Track->GetNonPointsOppositeLinkPos(NextElementEntryPos);
2090  // this is only for signals so no need to worry about points ambiguity
2091  TrainOnNextElement = (NextTrackElement.TrainIDOnElement > -1);
2092  StopSignalAtNextElement = ((NextTrackElement.Config[NextElementExitPos] == Signal) && (NextTrackElement.Attribute == 0));
2093  }
2094  if(TrainAtStopLinkPos1 || TrainAtStopLinkPos2 || (ForwardConnection && (TrainOnNextElement || StopSignalAtNextElement)))
2095  {
2096  if(TTVPos > 0)
2097  {
2099  ActionVectorEntryPtr += TTVPos;
2100  }
2101  if(StopRequired)
2102  {
2103  StoppedAtLocation = true;
2104  StoppedAtSignal = false;
2105  // may have been set earlier at line 925 so need to reset as
2106  // StoppedAtLocation takes precedence and don't want both set at same time or have flashing graphic
2107  // in zoom out mode
2108  if(!TrainFailed)
2109  {
2111  // pale green
2112  }
2115  {
2116  TimeTimeLocArrived = true;
2117  // used in case of later signaller control, when need to know
2118  // whether had arrived or not, to avoid sending the arrival
2119  // message twice, see TInterface::TimetableControl1Click
2120  }
2121  }
2122  else
2123  {
2125  }
2127  {
2129  }
2130  // don't alter ActionVectorEntryPtr if at a TimeTimeLoc (& can't be anything else other than TimeLoc or PassTime after calling NameInTimetableBeforeCDT successfully)
2132  }
2133  }
2134  }
2135  }
2136  }
2137  if(Straddle == MidLag)
2138  {
2139  Straddle = LeadMidLag;
2140  FirstHalfMove = false;
2141  }
2142  else if(Straddle == LeadMidLag)
2143  {
2144  Straddle = LeadMid;
2145  FirstHalfMove = true;
2146  }
2147  else if(Straddle == LeadMid)
2148  {
2149  throw Exception("Error, Straddle shouldn't be LeadMid prior to resetting at exit from UpdateTrain");
2150  }
2151  if(TrainFailurePending) // ok, moving but PlotElements set above
2152  {
2153  TrainHasFailed(7);
2154  }
2155  Display->Update();
2156  // need to keep this since Update() not called for PlotSmallOutput as too slow
2157  Utilities->CallLogPop(661);
2158 }
2159 
2160 // ----------------------------------------------------------------------------
2161 
2162 Graphics::TBitmap *TTrain::SetOneGraphicCode(char CodeChar)
2163 {
2164  switch(CodeChar)
2165  {
2166  case '0':
2167  return(RailGraphics->Code0);
2168 
2169  case '1':
2170  return(RailGraphics->Code1);
2171 
2172  case '2':
2173  return(RailGraphics->Code2);
2174 
2175  case '3':
2176  return(RailGraphics->Code3);
2177 
2178  case '4':
2179  return(RailGraphics->Code4);
2180 
2181  case '5':
2182  return(RailGraphics->Code5);
2183 
2184  case '6':
2185  return(RailGraphics->Code6);
2186 
2187  case '7':
2188  return(RailGraphics->Code7);
2189 
2190  case '8':
2191  return(RailGraphics->Code8);
2192 
2193  case '9':
2194  return(RailGraphics->Code9);
2195 
2196  case 'A':
2197  return(RailGraphics->CodeA);
2198 
2199  case 'B':
2200  return(RailGraphics->CodeB);
2201 
2202  case 'C':
2203  return(RailGraphics->CodeC);
2204 
2205  case 'D':
2206  return(RailGraphics->CodeD);
2207 
2208  case 'E':
2209  return(RailGraphics->CodeE);
2210 
2211  case 'F':
2212  return(RailGraphics->CodeF);
2213 
2214  case 'G':
2215  return(RailGraphics->CodeG);
2216 
2217  case 'H':
2218  return(RailGraphics->CodeH);
2219 
2220  case 'I':
2221  return(RailGraphics->CodeI);
2222 
2223  case 'J':
2224  return(RailGraphics->CodeJ);
2225 
2226  case 'K':
2227  return(RailGraphics->CodeK);
2228 
2229  case 'L':
2230  return(RailGraphics->CodeL);
2231 
2232  case 'M':
2233  return(RailGraphics->CodeM);
2234 
2235  case 'N':
2236  return(RailGraphics->CodeN);
2237 
2238  case 'O':
2239  return(RailGraphics->CodeO);
2240 
2241  case 'P':
2242  return(RailGraphics->CodeP);
2243 
2244  case 'Q':
2245  return(RailGraphics->CodeQ);
2246 
2247  case 'R':
2248  return(RailGraphics->CodeR);
2249 
2250  case 'S':
2251  return(RailGraphics->CodeS);
2252 
2253  case 'T':
2254  return(RailGraphics->CodeT);
2255 
2256  case 'U':
2257  return(RailGraphics->CodeU);
2258 
2259  case 'V':
2260  return(RailGraphics->CodeV);
2261 
2262  case 'W':
2263  return(RailGraphics->CodeW);
2264 
2265  case 'X':
2266  return(RailGraphics->CodeX);
2267 
2268  case 'Y':
2269  return(RailGraphics->CodeY);
2270 
2271  case 'Z':
2272  return(RailGraphics->CodeZ);
2273 
2274  case 'a':
2275  return(RailGraphics->Code_a);
2276 
2277  case 'b':
2278  return(RailGraphics->Code_b);
2279 
2280  case 'c':
2281  return(RailGraphics->Code_c);
2282 
2283  case 'd':
2284  return(RailGraphics->Code_d);
2285 
2286  case 'e':
2287  return(RailGraphics->Code_e);
2288 
2289  case 'f':
2290  return(RailGraphics->Code_f);
2291 
2292  case 'g':
2293  return(RailGraphics->Code_g);
2294 
2295  case 'h':
2296  return(RailGraphics->Code_h);
2297 
2298  case 'i':
2299  return(RailGraphics->Code_i);
2300 
2301  case 'j':
2302  return(RailGraphics->Code_j);
2303 
2304  case 'k':
2305  return(RailGraphics->Code_k);
2306 
2307  case 'l':
2308  return(RailGraphics->Code_l);
2309 
2310  case 'm':
2311  return(RailGraphics->Code_m);
2312 
2313  case 'n':
2314  return(RailGraphics->Code_n);
2315 
2316  case 'o':
2317  return(RailGraphics->Code_o);
2318 
2319  case 'p':
2320  return(RailGraphics->Code_p);
2321 
2322  case 'q':
2323  return(RailGraphics->Code_q);
2324 
2325  case 'r':
2326  return(RailGraphics->Code_r);
2327 
2328  case 's':
2329  return(RailGraphics->Code_s);
2330 
2331  case 't':
2332  return(RailGraphics->Code_t);
2333 
2334  case 'u':
2335  return(RailGraphics->Code_u);
2336 
2337  case 'v':
2338  return(RailGraphics->Code_v);
2339 
2340  case 'w':
2341  return(RailGraphics->Code_w);
2342 
2343  case 'x':
2344  return(RailGraphics->Code_x);
2345 
2346  case 'y':
2347  return(RailGraphics->Code_y);
2348 
2349  case 'z':
2350  return(RailGraphics->Code_z);
2351 
2352  default:
2353  return(RailGraphics->TempHeadCode);
2354  }
2355 }
2356 
2357 // ----------------------------------------------------------------------------
2358 
2359 void TTrain::SetHeadCodeGraphics(int Caller, AnsiString Code)
2360 {
2361  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SetHeadCodeGraphics," + HeadCode);
2362  if(Code.Length() != 4)
2363  {
2364  TrainController->StopTTClockMessage(62, "Headcode Incorrect length");
2365  }
2366  for(int x = 1; x < 5; x++) // AnsiString indices start at 1
2367  {
2368  HeadCodeGrPtr[x - 1]->Assign(SetOneGraphicCode(Code[x]));
2369  }
2370  if(BackgroundColour != clB5G5R5)
2371  // i.e. not the basic graphic colour as loaded from resource file
2372  {
2373  for(int x = 0; x < 4; x++)
2374  {
2376  }
2377  }
2378  Utilities->CallLogPop(1484);
2379 }
2380 
2381 // ----------------------------------------------------------------------------
2382 
2383 void TTrain::GetLeadElement(int Caller)
2384 // assumes Mid & Lag already set, sets LeadElement,
2385 // LeadEntryPos, LeadExitPos & DerailPending (don't want to act on it immediately)
2386 {
2387  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetLeadElement," + HeadCode);
2388  DerailPending = false;
2392  {
2393  // attr 0=straight, - links 0 & 1 (0 = lead)
2394  // attr 1=diverging, - links 2 & 3 (2 = lead)
2395  // set appropriate next element or derail - use a subroutine & return element & bool for derail
2396  // points always have links 0 & 2 = lead, link 1 = trailing straight, link 3 = training diverging
2397 
2398  // if enter at lead, exit at whatever attr set at
2399  // if enter at lag, exit at lead, but set derail wrt attribute
2400  if((LeadEntryPos == 0) && (Track->TrackElementAt(272, LeadElement).Attribute == 0))
2401  {
2402  LeadExitPos = 1;
2403  }
2404 
2405  // strictly speaking shouldn't need to set to 0 and 2 correctly since TrackIsInARoute caters for both, but
2406  // best to be on safe side
2407  else if(LeadEntryPos == 0)
2408  {
2409  LeadEntryPos = 2;
2410  LeadExitPos = 3;
2411  }
2412  else if((LeadEntryPos == 2) && (Track->TrackElementAt(273, LeadElement).Attribute == 0))
2413  {
2414  LeadEntryPos = 0;
2415  LeadExitPos = 1;
2416  }
2417  else if(LeadEntryPos == 2)
2418  {
2419  LeadExitPos = 3;
2420  }
2421 
2422  else if((LeadEntryPos == 1) && (Track->TrackElementAt(274, LeadElement).Attribute == 0))
2423  {
2424  LeadExitPos = 0;
2425  }
2426  else if(LeadEntryPos == 1)
2427  {
2428  LeadExitPos = 0;
2429  DerailPending = true;
2430  }
2431  else if((LeadEntryPos == 3) && (Track->TrackElementAt(275, LeadElement).Attribute == 0))
2432  {
2433  LeadExitPos = 0;
2434  DerailPending = true;
2435  }
2436  else if(LeadEntryPos == 3)
2437  {
2438  LeadExitPos = 0;
2439  }
2440  }
2441  else if(LeadEntryPos == 0)
2442  {
2443  LeadExitPos = 1;
2444  }
2445  else if(LeadEntryPos == 1)
2446  {
2447  LeadExitPos = 0;
2448  }
2449  else if(LeadEntryPos == 2)
2450  {
2451  LeadExitPos = 3;
2452  }
2453  else if(LeadEntryPos == 3)
2454  {
2455  LeadExitPos = 2;
2456  }
2457  // TTrackElement TrackElement = Track->TrackElementAt(276, LeadElement);
2458 /* signal check moved to Update() function
2459  if((TrackElement.TrackType == SignalPost) && (TrackElement.Config[LeadExitPos] == Signal)
2460  && (TrackElement.Attribute == 0))//0 = red
2461  {
2462  StoppedAtSignal = true; //comment out for test of locked route graphic replot
2463  }
2464  else
2465  {
2466  StoppedAtSignal = false;
2467  }
2468 */
2469  Utilities->CallLogPop(662);
2470 }
2471 
2472 // ----------------------------------------------------------------------------
2473 
2474 void TTrain::GetOffsetValues(int Caller, int &HOffset, int &VOffset, int Link) const
2475 {
2476  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetOffsetValues," + AnsiString(Link) + "," + HeadCode);
2477  switch(Link)
2478  {
2479  case 1:
2480  {
2481  HOffset = 0;
2482  VOffset = 0;
2483  break;
2484  }
2485 
2486  case 2:
2487  {
2488  HOffset = 4;
2489  VOffset = 0;
2490  break;
2491  }
2492 
2493  case 3:
2494  {
2495  HOffset = 8;
2496  VOffset = 0;
2497  break;
2498  }
2499 
2500  case 4:
2501  {
2502  HOffset = 0;
2503  VOffset = 4;
2504  break;
2505  }
2506 
2507  case 6:
2508  {
2509  HOffset = 8;
2510  VOffset = 4;
2511  break;
2512  }
2513 
2514  case 7:
2515  {
2516  HOffset = 0;
2517  VOffset = 8;
2518  break;
2519  }
2520 
2521  case 8:
2522  {
2523  HOffset = 4;
2524  VOffset = 8;
2525  break;
2526  }
2527 
2528  case 9:
2529  {
2530  HOffset = 8;
2531  VOffset = 8;
2532  break;
2533  }
2534 
2535  default:
2536  {
2537  throw Exception("Error in GetOffsetValues - Link value wrong");
2538  }
2539  }
2540  Utilities->CallLogPop(674);
2541 }
2542 
2543 // ---------------------------------------------------------------------------
2544 
2545 bool TTrain::LowEntryValue(int EntryLink) const
2546 {
2547 /* returns true if EntryLink is 1, 2, 4 or 7, in these circumstances the front of the train (i.e.
2548  the character that is red or blue) is the last character of the headcode, otherwise it's the first character of the headcode
2549 */
2550  if((EntryLink == 1) || (EntryLink == 2) || (EntryLink == 4) || (EntryLink == 7))
2551  {
2552  return(true);
2553  }
2554  else
2555  {
2556  return(false);
2557  }
2558 }
2559 
2560 // ---------------------------------------------------------------------------
2561 
2562 void TTrain::PickUpBackgroundBitmap(int Caller, int HOffset, int VOffset, int Element, int EntryPos, Graphics::TBitmap *GraphicPtr) const
2563 {
2564  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PickUpBackgroundBitmap," + AnsiString(HOffset) + "," +
2565  AnsiString(VOffset) + "," + AnsiString(Element) + "," + AnsiString(EntryPos) + "," + HeadCode);
2566  Graphics::TBitmap *EXGraphicPtr = RailGraphics->bmTransparentBgnd;
2567  // default values
2568  Graphics::TBitmap *EntryDirectionGraphicPtr = RailGraphics->bmTransparentBgnd;
2569 
2570  TAllRoutes::TRouteType RouteType;
2571 
2572  RouteType = AllRoutes->GetRouteTypeAndGraphics(11, Element, EntryPos, EXGraphicPtr, EntryDirectionGraphicPtr);
2573 
2574  TRect SourceRect, DestRect;
2575 
2576  DestRect.init(0, 0, 8, 8); // initialise left, top, right, bottom
2577  // note right and bottom rect co-ordinates are 1 greater than the pixel area
2578  SourceRect.init(HOffset, VOffset, HOffset + 8, VOffset + 8);
2579  Graphics::TBitmap *TempGraphic = new Graphics::TBitmap;
2580 
2581  TempGraphic->PixelFormat = pf8bit;
2582  TempGraphic->Width = 16;
2583  TempGraphic->Height = 16;
2584  TTrackElement TempElement = Track->TrackElementAt(286, Element);
2585 
2586  if(TempElement.TrackType == Points)
2587  {
2588  TempGraphic->Assign(TempElement.GraphicPtr);
2589  TempGraphic->Transparent = true;
2590  TempGraphic->TransparentColor = Utilities->clTransparent;
2591  if(RouteType == TAllRoutes::AutoSigsRoute)
2592  {
2593  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2594  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2595  }
2596  else
2597  {
2598  TempGraphic->Canvas->Draw(0, 0, Track->GetFilletGraphic(1, TempElement)); // add fillet
2599  }
2600  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2601  }
2602  else if(TempElement.TrackType == GapJump) // plot set gap
2603  {
2604  if(TempElement.SpeedTag == 88)
2605  {
2606  TempGraphic->Assign(RailGraphics->gl88set);
2607  }
2608  else if(TempElement.SpeedTag == 89)
2609  {
2610  TempGraphic->Assign(RailGraphics->gl89set);
2611  }
2612  else if(TempElement.SpeedTag == 90)
2613  {
2614  TempGraphic->Assign(RailGraphics->gl90set);
2615  }
2616  else if(TempElement.SpeedTag == 91)
2617  {
2618  TempGraphic->Assign(RailGraphics->gl91set);
2619  }
2620  else if(TempElement.SpeedTag == 92)
2621  {
2622  TempGraphic->Assign(RailGraphics->gl92set);
2623  }
2624  else if(TempElement.SpeedTag == 93)
2625  {
2626  TempGraphic->Assign(RailGraphics->bm93set);
2627  }
2628  else if(TempElement.SpeedTag == 94)
2629  {
2630  TempGraphic->Assign(RailGraphics->bm94set);
2631  }
2632  else if(TempElement.SpeedTag == 95)
2633  {
2634  TempGraphic->Assign(RailGraphics->gl95set);
2635  }
2636  TempGraphic->Transparent = true;
2637  TempGraphic->TransparentColor = Utilities->clTransparent;
2638  if(RouteType == TAllRoutes::AutoSigsRoute)
2639  {
2640  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2641  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2642  }
2643  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2644  }
2645  // new for version 0.6
2646  else if(TempElement.TrackType == SignalPost)
2647  {
2648  if(TempElement.SigAspect == TTrackElement::GroundSignal)
2649  {
2650  for(int x = 0; x < 40; x++)
2651  {
2652  if((Track->SigTableGroundSignal[x].SpeedTag == TempElement.SpeedTag) && (Track->SigTableGroundSignal[x].Attribute == 0))
2653  // need to stop aspect
2654  {
2655  TempGraphic->Assign(Track->SigTableGroundSignal[x].SigPtr);
2656  break;
2657  }
2658  }
2659  }
2660  else // normal signal
2661  {
2662  TempGraphic->Assign(TempElement.GraphicPtr);
2663  // GraphicPtr set to normal signal in a signal track element
2664  }
2665  TempGraphic->Transparent = true;
2666  TempGraphic->TransparentColor = Utilities->clTransparent;
2667  if(RouteType == TAllRoutes::AutoSigsRoute)
2668  {
2669  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2670  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2671  }
2672  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2673  }
2674  else
2675  {
2676  // first check if there's a NamedNonStationLocation element at that position & if so pick up that as the background
2677  // can't name points gaps or signals so 'else' OK
2678  bool FoundFlag;
2679  TTrack::TIMPair IMPair = Track->GetVectorPositionsFromInactiveTrackMap(4, TempElement.HLoc, TempElement.VLoc, FoundFlag);
2680  if(FoundFlag)
2681  {
2683  {
2684  GraphicPtr->Canvas->CopyRect(DestRect, Track->InactiveTrackElementAt(26, IMPair.first).GraphicPtr->Canvas, SourceRect);
2685  TempGraphic->Assign(RailGraphics->bmName);
2686  TempGraphic->Transparent = true;
2687  TempGraphic->TransparentColor = Utilities->clTransparent;
2688  if(RouteType == TAllRoutes::AutoSigsRoute)
2689  {
2690  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2691  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2692  }
2693  else
2694  {
2695  TempGraphic->Canvas->Draw(0, 0, TempElement.GraphicPtr);
2696  }
2697  // draw track on top
2698  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2699  }
2700  else if(Track->InactiveTrackElementAt(116, IMPair.first).TrackType == LevelCrossing)
2701  {
2702  TempGraphic->Assign(TempElement.GraphicPtr);
2703  TempGraphic->Transparent = true;
2704  TempGraphic->TransparentColor = Utilities->clTransparent;
2705  // note that can't be an AutoSigsRoute
2706  // now overlay the LC central portion
2707  int BDVectorPos = -1; //not used
2708  if(Track->AnyLinkedBarrierDownVectorManual(0, Track->InactiveTrackElementAt(130, IMPair.first).HLoc, Track->InactiveTrackElementAt(131, IMPair.first).VLoc, BDVectorPos))
2709  {
2710  TempGraphic->Canvas->Draw(0, 0, RailGraphics->LCPlainMan);
2711  }
2712  else
2713  {
2714  TempGraphic->Canvas->Draw(0, 0, RailGraphics->LCPlain);
2715  }
2716  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2717  }
2718  else
2719  {
2720  TempGraphic->Assign(TempElement.GraphicPtr);
2721  TempGraphic->Transparent = true;
2722  TempGraphic->TransparentColor = Utilities->clTransparent;
2723  if(RouteType == TAllRoutes::AutoSigsRoute)
2724  {
2725  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2726  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2727  }
2728  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2729  }
2730  }
2731  else
2732  {
2733  TempGraphic->Assign(TempElement.GraphicPtr);
2734  TempGraphic->Transparent = true;
2735  TempGraphic->TransparentColor = Utilities->clTransparent;
2736  if(RouteType == TAllRoutes::AutoSigsRoute)
2737  {
2738  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2739  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2740  }
2741  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2742  }
2743  }
2744  delete TempGraphic;
2745  Utilities->CallLogPop(675);
2746 }
2747 
2748 // ---------------------------------------------------------------------------
2749 
2750 // This was an attempt to pick up the actual 8x8 graphic from the display, so that text & user graphics would show as soon as the train passed, and overwrite it with the
2751 // reconstructed track, and it works ok but for the little arrows showing route directions at start and end, which extend beyond the track. It doesn't matter for autosig
2752 // routes because they are replotted (alomg with the direction arrows) but for others they shouldn't be. Leave in in case an easy way to remove these pointers comes to mind.
2753 /*
2754 
2755  void TTrain::PickUpBackgroundBitmap(int Caller, int HOffset, int VOffset, int Element, int EntryPos, Graphics::TBitmap *GraphicPtr) const
2756  {
2757  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PickUpBackgroundBitmap," + AnsiString(HOffset) +
2758  "," + AnsiString(VOffset) + "," + AnsiString(Element) + "," + AnsiString(EntryPos) + "," + HeadCode);
2759  TAllRoutes::TRouteType RouteType;
2760  Graphics::TBitmap *EXGraphicPtr = RailGraphics->bmTransparentBgnd;
2761  // default values
2762  Graphics::TBitmap *EntryDirectionGraphicPtr = RailGraphics->bmTransparentBgnd;
2763  TTrackElement TempElement = Track->TrackElementAt(, Element); //this is a copy of the element passed into the function
2764  TRect SourceRect, DestRect, ScreenSourceRect;
2765  RouteType = AllRoutes->GetRouteTypeAndGraphics(7, Element, EntryPos, EXGraphicPtr, EntryDirectionGraphicPtr);
2766 
2767  DestRect.init(0, 0, 8, 8); //initialise left, top, right, bottom
2768  // note right and bottom rect co-ordinates are 1 greater than the pixel area
2769  SourceRect.init(HOffset, VOffset, HOffset + 8, VOffset + 8);
2770 
2771  //add text & user graphics if any to *GraphicPtr prior to adding the track
2772  int Left = ((TempElement.HLoc - Display->DisplayOffsetH) * 16) + HOffset;
2773  int Top = ((TempElement.VLoc - Display->DisplayOffsetV) * 16) + VOffset;
2774  int Right = Left + 8;
2775  int Bottom = Top + 8;
2776  ScreenSourceRect.init(Left, Top, Right, Bottom);
2777  GraphicPtr->Canvas->CopyMode = cmSrcCopy;
2778  GraphicPtr->Canvas->CopyRect(DestRect, Display->GetImage()->Canvas, ScreenSourceRect);
2779 
2780  Graphics::TBitmap *TempGraphic = new Graphics::TBitmap; //this will hold the 16x16 reconstructed element, prior to transfer of the 8x8 bit to *GraphicPtr
2781  TempGraphic->PixelFormat = pf8bit;
2782  TempGraphic->Width = 16;
2783  TempGraphic->Height = 16;
2784 
2785  Graphics::TBitmap *SourceGraphic = new Graphics::TBitmap; //this will hold the 8x8 element segment from TempGraphic - needed because to keep transparency have to use Draw, not CopyRect
2786  SourceGraphic->PixelFormat = pf8bit;
2787  SourceGraphic->Width = 16;
2788  SourceGraphic->Height = 16;
2789  SourceGraphic->Transparent = true;
2790  SourceGraphic->TransparentColor = Utilities->clTransparent;
2791 
2792  if (TempElement.TrackType == Points)
2793  {
2794  TempGraphic->Assign(TempElement.GraphicPtr);
2795  TempGraphic->Transparent = true;
2796  TempGraphic->TransparentColor = Utilities->clTransparent;
2797  if (RouteType == TAllRoutes::AutoSigsRoute)
2798  {
2799  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2800  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2801  }
2802  else
2803  {
2804  TempGraphic->Canvas->Draw(0, 0, Track->GetFilletGraphic(1, TempElement)); // add fillet
2805  }
2806  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2807  }
2808  else if (TempElement.TrackType == GapJump) // plot set gap
2809  {
2810  if (TempElement.SpeedTag == 88)
2811  TempGraphic->Assign(RailGraphics->gl88set);
2812  else if (TempElement.SpeedTag == 89)
2813  TempGraphic->Assign(RailGraphics->gl89set);
2814  else if (TempElement.SpeedTag == 90)
2815  TempGraphic->Assign(RailGraphics->gl90set);
2816  else if (TempElement.SpeedTag == 91)
2817  TempGraphic->Assign(RailGraphics->gl91set);
2818  else if (TempElement.SpeedTag == 92)
2819  TempGraphic->Assign(RailGraphics->gl92set);
2820  else if (TempElement.SpeedTag == 93)
2821  TempGraphic->Assign(RailGraphics->bm93set);
2822  else if (TempElement.SpeedTag == 94)
2823  TempGraphic->Assign(RailGraphics->bm94set);
2824  else if (TempElement.SpeedTag == 95)
2825  TempGraphic->Assign(RailGraphics->gl95set);
2826  TempGraphic->Transparent = true;
2827  TempGraphic->TransparentColor = Utilities->clTransparent;
2828  if (RouteType == TAllRoutes::AutoSigsRoute) {
2829  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2830  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2831  }
2832  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2833  }
2834  // new for version 0.6
2835  else if (TempElement.TrackType == SignalPost)
2836  {
2837  if (TempElement.SigAspect == TTrackElement::GroundSignal)
2838  {
2839  for (int x = 0; x < 40; x++)
2840  {
2841  if ((Track->SigTableGroundSignal[x].SpeedTag == TempElement.SpeedTag) && (Track->SigTableGroundSignal[x].Attribute == 0))
2842  // need to stop aspect
2843  {
2844  TempGraphic->Assign(Track->SigTableGroundSignal[x].SigPtr);
2845  break;
2846  }
2847  }
2848  }
2849  else // normal signal
2850  {
2851  TempGraphic->Assign(TempElement.GraphicPtr);
2852  // GraphicPtr set to normal signal in a signal track element
2853  }
2854  TempGraphic->Transparent = true;
2855  TempGraphic->TransparentColor = Utilities->clTransparent;
2856  if (RouteType == TAllRoutes::AutoSigsRoute) {
2857  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2858  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2859  }
2860  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2861  }
2862  else {
2863  // first check if there's a NamedNonStationLocation element at that position & if so pick up that as the background
2864  // can't name points gaps or signals so 'else' OK
2865  bool FoundFlag;
2866  TTrack::TIMPair IMPair = Track->GetVectorPositionsFromInactiveTrackMap
2867  (4, TempElement.HLoc, TempElement.VLoc, FoundFlag);
2868  if (FoundFlag)
2869  {
2870  if (Track->InactiveTrackElementAt(, IMPair.first).TrackType == NamedNonStationLocation)
2871  {
2872  GraphicPtr->Canvas->CopyRect(DestRect,
2873  Track->InactiveTrackElementAt(, IMPair.first).GraphicPtr->Canvas, SourceRect);
2874  TempGraphic->Assign(RailGraphics->bmName);
2875  TempGraphic->Transparent = true;
2876  TempGraphic->TransparentColor = Utilities->clTransparent;
2877  if (RouteType == TAllRoutes::AutoSigsRoute)
2878  {
2879  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2880  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2881  }
2882  else
2883  TempGraphic->Canvas->Draw(0, 0, TempElement.GraphicPtr);
2884  // draw track on top
2885  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas,
2886  SourceRect);
2887  }
2888  else if (Track->InactiveTrackElementAt(, IMPair.first).TrackType == LevelCrossing) {
2889  TempGraphic->Assign(TempElement.GraphicPtr);
2890  TempGraphic->Transparent = true;
2891  TempGraphic->TransparentColor = Utilities->clTransparent;
2892  // note that can't be an AutoSigsRoute
2893  // now overlay the LC central portion
2894  TempGraphic->Canvas->Draw(0, 0, RailGraphics->LCPlain);
2895  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas,
2896  SourceRect);
2897  }
2898  else {
2899  TempGraphic->Assign(TempElement.GraphicPtr);
2900  TempGraphic->Transparent = true;
2901  TempGraphic->TransparentColor = Utilities->clTransparent;
2902  if (RouteType == TAllRoutes::AutoSigsRoute) {
2903  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2904  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2905  }
2906  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas,
2907  SourceRect);
2908  }
2909  }
2910  else {
2911  TempGraphic->Assign(TempElement.GraphicPtr);
2912  TempGraphic->Transparent = true;
2913  TempGraphic->TransparentColor = Utilities->clTransparent;
2914  if (RouteType == TAllRoutes::AutoSigsRoute) {
2915  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2916  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2917  }
2918  SourceGraphic->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2919  GraphicPtr->Canvas->Draw(0, 0, SourceGraphic);
2920  }
2921  }
2922  delete TempGraphic;
2923  delete SourceGraphic;
2924  Utilities->CallLogPop();
2925  }
2926 */
2927 // ---------------------------------------------------------------------------
2928 
2929 void TTrain::PlotTrainGraphic(int Caller, int ArrayNumber, TDisplay *Disp)
2930 {
2931  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PlotTrainGraphic," + AnsiString(ArrayNumber) + "," + HeadCode);
2932  if(PlotElement[ArrayNumber] == -1)
2933  {
2934  Utilities->CallLogPop(676);
2935  return; // not plotted yet
2936  }
2937  SetTrainElementID(0, PlotElement[ArrayNumber], PlotEntryPos[ArrayNumber]);
2938  // set before plot so gap flashing stops first
2939  Disp->PlotOutput(29, ((Track->TrackElementAt(295, PlotElement[ArrayNumber]).HLoc * 16) + HOffset[ArrayNumber]),
2940  ((Track->TrackElementAt(296, PlotElement[ArrayNumber]).VLoc * 16) + VOffset[ArrayNumber]), HeadCodePosition[ArrayNumber]);
2941  // Only need to set ID for leading element, stays set until train finally leaves the element
2942  Plotted = true;
2943  Utilities->CallLogPop(677);
2944 }
2945 
2946 // ---------------------------------------------------------------------------
2947 
2948 void TTrain::PlotBackgroundGraphic(int Caller, int ArrayNumber, TDisplay *Disp) const
2949 {
2950  Disp->PlotOutput(30, ((Track->TrackElementAt(297, PlotElement[ArrayNumber]).HLoc * 16) + HOffset[ArrayNumber]),
2951  ((Track->TrackElementAt(298, PlotElement[ArrayNumber]).VLoc * 16) + VOffset[ArrayNumber]), BackgroundPtr[ArrayNumber]);
2952 }
2953 
2954 // ---------------------------------------------------------------------------
2955 
2956 bool TTrain::BufferAtExit(int Caller, int Element, int ExitPos) const
2957 {
2958  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",BufferAtExit," + AnsiString(Element) + "," + AnsiString(ExitPos) + "," +
2959  HeadCode);
2960  if((Track->TrackElementAt(299, Element).TrackType == Buffers) && (Track->TrackElementAt(300, Element).Config[ExitPos] == End))
2961  {
2962  Utilities->CallLogPop(678);
2963  return(true);
2964  }
2965  else
2966  {
2967  Utilities->CallLogPop(679);
2968  return(false);
2969  }
2970 }
2971 
2972 // ---------------------------------------------------------------------------
2973 
2974 bool TTrain::ContinuationExit(int Caller, int Element, int ExitPos) const
2975 {
2976  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ContinuationExit," + AnsiString(Element) + "," + AnsiString(ExitPos) +
2977  "," + HeadCode);
2978  if((Track->TrackElementAt(301, Element).TrackType == Continuation) && (Track->TrackElementAt(302, Element).Config[ExitPos] == End))
2979  {
2980  Utilities->CallLogPop(680);
2981  return(true);
2982  }
2983  else
2984  {
2985  Utilities->CallLogPop(681);
2986  return(false);
2987  }
2988 }
2989 
2990 // ---------------------------------------------------------------------------
2991 
2992 bool TTrain::IsTrainIDOnBridgeTrackPos01(int Caller, unsigned int TrackVectorPosition)
2993 // test whether this train on a bridge on trackpos 0 & 1
2994 {
2995  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",IsTrainIDOnBridgeTrackPos01," + AnsiString(TrackVectorPosition) + "," +
2996  HeadCode);
2997  if(Track->TrackElementAt(303, TrackVectorPosition).TrackType != Bridge)
2998  {
2999  Utilities->CallLogPop(682);
3000  return(false);
3001  }
3002  // if(Track->TrackElementAt(304, TrackVectorPosition).TrainIDOnElement != TrainID) return false; No, if a bridge could be one of 2 TrainIDs
3003  if(Track->TrackElementAt(305, TrackVectorPosition).TrainIDOnBridgeTrackPos01 == TrainID)
3004  {
3005  if(Track->TrackElementAt(306, TrackVectorPosition).TrainIDOnBridgeTrackPos23 == TrainID)
3006  {
3007  throw Exception("Error, same train on two different bridge tracks");
3008  }
3009  else
3010  {
3011  Utilities->CallLogPop(684);
3012  return(true);
3013  }
3014  }
3015  else
3016  {
3017  Utilities->CallLogPop(685);
3018  return(false);
3019  }
3020 }
3021 
3022 // ---------------------------------------------------------------------------
3023 
3024 bool TTrain::IsTrainIDOnBridgeTrackPos23(int Caller, unsigned int TrackVectorPosition)
3025 // test whether this train on a bridge on trackpos 2 & 3
3026 {
3027  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",IsTrainIDOnBridgeTrackPos23," + AnsiString(TrackVectorPosition) + "," +
3028  HeadCode);
3029  if(Track->TrackElementAt(307, TrackVectorPosition).TrackType != Bridge)
3030  {
3031  Utilities->CallLogPop(686);
3032  return(false);
3033  }
3034  // if(Track->TrackElementAt(308, TrackVectorPosition).TrainIDOnElement != TrainID) return false; No, if a bridge could be one of 2 TrainIDs
3035  if(Track->TrackElementAt(309, TrackVectorPosition).TrainIDOnBridgeTrackPos23 == TrainID)
3036  {
3037  // don't carry out check for train on tracks 0 & 1 else will enter an infinite loop if train on both
3038  Utilities->CallLogPop(687);
3039  return(true);
3040  }
3041  else
3042  {
3043  Utilities->CallLogPop(688);
3044  return(false);
3045  }
3046 }
3047 
3048 // ---------------------------------------------------------------------------
3049 
3050 void TTrain::SetTrainElementID(int Caller, unsigned int TrackVectorPosition, int EntryPos)
3051 {
3052  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SetTrainElementID," + AnsiString(TrackVectorPosition) + "," +
3053  AnsiString(EntryPos) + "," + HeadCode);
3054  Track->TrackElementAt(310, TrackVectorPosition).TrainIDOnElement = TrainID;
3055 
3056  // unplot GapFlash graphics if land on flashing gap (this done before train plotted - see PlotTrainGraphic)
3057  if(Track->GapFlashFlag)
3058  {
3060  {
3063  Track->GapFlashFlag = false;
3064  }
3065  }
3066  if(Track->TrackElementAt(311, TrackVectorPosition).TrackType == Bridge)
3067  {
3068  if(EntryPos == -1)
3069  {
3070  throw Exception("Error, TrackVectorPosition set but not EntryPos in SetTrainElementID");
3071  }
3072  if(EntryPos < 2)
3073  {
3074  Track->TrackElementAt(312, TrackVectorPosition).TrainIDOnBridgeTrackPos01 = TrainID;
3075  }
3076  else
3077  {
3078  Track->TrackElementAt(313, TrackVectorPosition).TrainIDOnBridgeTrackPos23 = TrainID;
3079  }
3080  }
3081  Utilities->CallLogPop(690);
3082 }
3083 
3084 // ---------------------------------------------------------------------------
3085 
3086 void TTrain::ResetTrainElementID(int Caller, unsigned int TrackVectorPosition, int EntryPos)
3087 {
3088  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ResetTrainElementID," + AnsiString(TrackVectorPosition) + "," +
3089  AnsiString(EntryPos) + "," + HeadCode);
3090  if(Track->TrackElementAt(314, TrackVectorPosition).TrackType != Bridge)
3091  {
3092  Track->TrackElementAt(315, TrackVectorPosition).TrainIDOnElement = -1;
3093  }
3094  else
3095  {
3096  if(EntryPos == -1)
3097  {
3098  throw Exception("Error, TrackVectorPosition set but not EntryPos in ResetTrainElementID");
3099  }
3100  if(EntryPos < 2)
3101  {
3102  Track->TrackElementAt(316, TrackVectorPosition).TrainIDOnBridgeTrackPos01 = -1;
3103  }
3104  else
3105  {
3106  Track->TrackElementAt(317, TrackVectorPosition).TrainIDOnBridgeTrackPos23 = -1;
3107  }
3108  if((EntryPos < 2) && (Track->TrackElementAt(318, TrackVectorPosition).TrainIDOnBridgeTrackPos23 > -1))
3109  // i.e. other train on track 2&3
3110  {
3111  Track->TrackElementAt(319, TrackVectorPosition).TrainIDOnElement = Track->TrackElementAt(320, TrackVectorPosition).TrainIDOnBridgeTrackPos23;
3112  }
3113  else if((EntryPos > 1) && (Track->TrackElementAt(321, TrackVectorPosition).TrainIDOnBridgeTrackPos01 > -1))
3114  // i.e. other train on track 1&2
3115  {
3116  Track->TrackElementAt(322, TrackVectorPosition).TrainIDOnElement = Track->TrackElementAt(323, TrackVectorPosition).TrainIDOnBridgeTrackPos01;
3117  }
3118  else
3119  {
3120  Track->TrackElementAt(324, TrackVectorPosition).TrainIDOnElement = -1;
3121  }
3122  }
3123  Utilities->CallLogPop(691);
3124 }
3125 
3126 // ---------------------------------------------------------------------------
3127 
3128 void TTrain::PlotAlternativeTrackRouteGraphic(int Caller, unsigned int ElementVecNum, int ElementEntryPos, int HOffset, int VOffset, TStraddle StraddleValue)
3129 {
3130  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PlotAlternativeTrackRouteGraphic," + AnsiString(ElementVecNum) + "," +
3131  AnsiString(ElementEntryPos) + "," + AnsiString(HOffset) + "," + AnsiString(VOffset) + "," + AnsiString(StraddleValue) + "," + HeadCode);
3132  int LockedVectorNumber;
3133 
3134  if(Track->TrackElementAt(325, ElementVecNum).TrackType != Bridge)
3135  // && (Track->TrackElementAt(326, ElementVecNum).TrackType != Crossover))
3136  {
3137  // only applies for a bridge as there can't be (or shouldn't be) 2 routes on an element that isn't a bridge
3138  Utilities->CallLogPop(692);
3139  return;
3140  }
3141  if(AllRoutes->TrackIsInARoute(0, ElementVecNum, (3 - ElementEntryPos)))
3142  // i.e other track is in a marked route
3143  // LinkPos doesn't have to be the entry position for the above check
3144  {
3145  TRect SourceRect, DestRect;
3146  DestRect.init(0, 0, 8, 8); // left, top, right, bottom
3147  // note right and bottom rect co-ordinates are 1 greater than the pixel area
3148  SourceRect.init(HOffset, VOffset, HOffset + 8, VOffset + 8);
3149  // identify the route element for the other track
3150  TAllRoutes::TRouteElementPair RoutePair1, RoutePair2;
3151  RoutePair1 = AllRoutes->GetRouteElementDataFromRoute2MultiMap(13, Track->TrackElementAt(327, ElementVecNum).HLoc,
3152  Track->TrackElementAt(328, ElementVecNum).VLoc, RoutePair2);
3153  int FirstELink, SecondELink = -1;
3154  FirstELink = AllRoutes->GetFixedRouteAt(149, RoutePair1.first).GetFixedPrefDirElementAt(159, RoutePair1.second).GetELink();
3155  // must be at least one
3156  if(RoutePair2.first > -1)
3157  {
3158  SecondELink = AllRoutes->GetFixedRouteAt(150, RoutePair2.first).GetFixedPrefDirElementAt(160, RoutePair2.second).GetELink();
3159  }
3160  TPrefDirElement RouteElement;
3161  // Graphics::TBitmap *RouteGraphic;
3162  if(FirstELink == Track->TrackElementAt(329, ElementVecNum).Link[ElementEntryPos])
3163  // i.e. other track is in RoutePair2
3164  {
3165  if(SecondELink == -1)
3166  {
3167  throw Exception("Error - Second ELink should be set but isn't in PlotAlternativeTrackRouteGraphic [1]");
3168  }
3169  if(SecondELink == Track->TrackElementAt(330, ElementVecNum).Link[ElementEntryPos])
3170  // error if both have same Link number
3171  {
3172  throw Exception("Error - First & Second ELinks have same value in PlotAlternativeTrackRouteGraphic");
3173  }
3174  // RouteGraphic = AllRoutes->GetFixedRouteAt(151, RoutePair2.first).GetFixedPrefDirElementAt(161, RoutePair2.second).GetEXGraphicPtr();
3175  RouteElement = AllRoutes->GetFixedRouteAt(152, RoutePair2.first).GetFixedPrefDirElementAt(162, RoutePair2.second);
3176  }
3177  else // other track is in RoutePair1
3178  {
3179  // RouteGraphic = AllRoutes->GetFixedRouteAt(153, RoutePair1.first).GetFixedPrefDirElementAt(163, RoutePair1.second).GetEXGraphicPtr();
3180  RouteElement = AllRoutes->GetFixedRouteAt(154, RoutePair1.first).GetFixedPrefDirElementAt(164, RoutePair1.second);
3181  }
3182  Graphics::TBitmap *DestGraphic = new Graphics::TBitmap;
3183  DestGraphic->PixelFormat = pf8bit;
3184  DestGraphic->Width = 8;
3185  DestGraphic->Height = 8;
3186  DestGraphic->Transparent = true;
3187  // has to be transparent or will overwrite the track that the train has just left
3188  DestGraphic->TransparentColor = Utilities->clTransparent;
3189  DestGraphic->Canvas->CopyRect(DestRect, RouteElement.GetRouteEXGraphicPtr()->Canvas, SourceRect);
3190  Display->PlotOutput(31, (Track->TrackElementAt(331, ElementVecNum).HLoc * 16) + HOffset,
3191  (Track->TrackElementAt(332, ElementVecNum).VLoc * 16) + VOffset, DestGraphic);
3192  // plot locked route marker for other route if appropriate
3193  TPrefDirElement PrefDirElement; // holder for next call, unused
3194  // plot locked route marker if appropriate, but only when train leaves element completely as this is a 16x16 graphic
3195  if(StraddleValue == LeadMidLag)
3196  {
3198  PrefDirElement, LockedVectorNumber))
3199  {
3200  Display->PlotOutput(32, (Track->TrackElementAt(333, ElementVecNum).HLoc * 16), (Track->TrackElementAt(334, ElementVecNum).VLoc * 16),
3201  RailGraphics->LockedRouteCancelPtr[RouteElement.GetELink()]);
3202  }
3203  }
3204  delete DestGraphic;
3205  }
3206  // but - there may be a train on the other track - if so need to replot it else the section of route overwrites it
3207  // also can only be a bridge or trains either have already or soon will crash
3208  if(Track->TrackElementAt(335, ElementVecNum).TrackType != Bridge)
3209  {
3210  Utilities->CallLogPop(695);
3211  return;
3212  }
3213  if(ElementEntryPos > 1) // other train is on track 01
3214  {
3215  if(Track->TrackElementAt(336, ElementVecNum).TrainIDOnBridgeTrackPos01 > -1)
3216  {
3218  }
3219  }
3220  else // other train is on track 23
3221  {
3222  if(Track->TrackElementAt(338, ElementVecNum).TrainIDOnBridgeTrackPos23 > -1)
3223  {
3225  }
3226  }
3227  Utilities->CallLogPop(696);
3228 }
3229 
3230 // ---------------------------------------------------------------------------
3231 
3232 void TTrain::CheckAndCancelRouteForWrongEndEntry(int Caller, int Element, int EntryPos)
3233 {
3234  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckAndCancelRouteForWrongEndEntry," + AnsiString(Element) + "," +
3235  AnsiString(EntryPos) + "," + HeadCode);
3236  int RouteNumber;
3237  bool WrongRoute = false;
3238  TPrefDirElement RouteElement;
3240  TAllRoutes::TRoute2MultiMapIterator Route2MultiMapIterator;
3241 
3242  if(AllRoutes->GetRouteTypeAndNumber(11, Element, EntryPos, RouteNumber) == TAllRoutes::NoRoute)
3243  // here if single track element & no route, or double track element with no route at EntryPos but still need to check if on points or a crossover on non-route track,
3244  // and force-erase route if so (bridge OK of course) note that GetRouteTypeAndNumber allows for points having an EntryPos of 0 or 2 & still returns correct values
3245  {
3246  if((Track->TrackElementAt(340, Element).TrackType == Crossover) || (Track->TrackElementAt(341, Element).TrackType == Points))
3247  {
3248  if(AllRoutes->GetRouteTypeAndNumber(12, Element, (3 - EntryPos), RouteNumber) != TAllRoutes::NoRoute)
3249  // (3-EntryPos) guarantees other route (0->3; 1->2; 2->1; 3->0)
3250  {
3251  if(AllRoutes->GetFixedRouteAt(179, RouteNumber).PrefDirSize() > 2)
3252  {
3253  // don't call for stub end routes
3255  }
3256  AllRoutes->GetModifiableRouteAt(13, RouteNumber).ForceCancelRoute(1);
3257  Utilities->CallLogPop(697);
3258  return;
3259  }
3260  }
3261  // also need to check for a route on a crossing diagonal
3262  TTrackElement TrackElement = Track->TrackElementAt(892, Element);
3263  int LinkNumber = TrackElement.Link[EntryPos];
3264  if((LinkNumber == 1) || (LinkNumber == 3) || (LinkNumber == 7) || (LinkNumber == 9))
3265  {
3266  if(AllRoutes->DiagonalFouledByRoute(0, TrackElement.HLoc, TrackElement.VLoc, LinkNumber))
3267  {
3268  // for LinkNumber = 1, potentially fouled diagonals are at H-1, V, Lk 3 & H, V-1, Lk 7
3269  bool LogActionErrorCalled = false;
3270  // to ensure only called once if have 2 routes on the 2 crossed diagonals
3271  if(LinkNumber == 1)
3272  {
3273  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(0, TrackElement.HLoc - 1, TrackElement.VLoc, 3, RouteNumber))
3274  {
3275  if(AllRoutes->GetFixedRouteAt(207, RouteNumber).PrefDirSize() > 2)
3276  {
3277  // don't call for stub end routes
3279  LogActionErrorCalled = true;
3280  }
3281  AllRoutes->GetModifiableRouteAt(20, RouteNumber).ForceCancelRoute(3);
3282  }
3283  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(1, TrackElement.HLoc, TrackElement.VLoc - 1, 7, RouteNumber))
3284  // not else in case have different routes on each diagonal, though shouldn't be possible
3285  {
3286  if(!LogActionErrorCalled && AllRoutes->GetFixedRouteAt(208, RouteNumber).PrefDirSize() > 2)
3287  {
3288  // don't call for stub end routes
3290  }
3291  AllRoutes->GetModifiableRouteAt(21, RouteNumber).ForceCancelRoute(4);
3292  }
3293  }
3294 
3295  // for LinkNumber = 3, potentially fouled diagonals are at H+1, V, Lk 1 & H, V-1 Lk 9
3296  else if(LinkNumber == 3)
3297  {
3298  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(2, TrackElement.HLoc + 1, TrackElement.VLoc, 1, RouteNumber))
3299  {
3300  if(AllRoutes->GetFixedRouteAt(209, RouteNumber).PrefDirSize() > 2)
3301  {
3302  // don't call for stub end routes
3304  LogActionErrorCalled = true;
3305  }
3306  AllRoutes->GetModifiableRouteAt(22, RouteNumber).ForceCancelRoute(5);
3307  }
3308  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(3, TrackElement.HLoc, TrackElement.VLoc - 1, 9, RouteNumber))
3309  // not else in case have different routes on each diagonal, though shouldn't be possible
3310  {
3311  if(!LogActionErrorCalled && AllRoutes->GetFixedRouteAt(210, RouteNumber).PrefDirSize() > 2)
3312  {
3313  // don't call for stub end routes
3315  }
3316  AllRoutes->GetModifiableRouteAt(23, RouteNumber).ForceCancelRoute(6);
3317  }
3318  }
3319 
3320  // for LinkNumber = 7, potentially fouled diagonals are at H-1, V, Lk 9 & H, V+1 Lk 1
3321  else if(LinkNumber == 7)
3322  {
3323  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(4, TrackElement.HLoc - 1, TrackElement.VLoc, 9, RouteNumber))
3324  {
3325  if(AllRoutes->GetFixedRouteAt(211, RouteNumber).PrefDirSize() > 2)
3326  {
3327  // don't call for stub end routes
3329  LogActionErrorCalled = true;
3330  }
3331  AllRoutes->GetModifiableRouteAt(24, RouteNumber).ForceCancelRoute(7);
3332  }
3333  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(5, TrackElement.HLoc, TrackElement.VLoc + 1, 1, RouteNumber))
3334  // not else in case have different routes on each diagonal, though shouldn't be possible
3335  {
3336  if(!LogActionErrorCalled && AllRoutes->GetFixedRouteAt(212, RouteNumber).PrefDirSize() > 2)
3337  {
3338  // don't call for stub end routes
3340  }
3341  AllRoutes->GetModifiableRouteAt(25, RouteNumber).ForceCancelRoute(8);
3342  }
3343  }
3344 
3345  // for LinkNumber = 9, potentially fouled diagonals are at H+1, V, Lk 7 & H, V+1 Lk 3
3346  else if(LinkNumber == 9)
3347  {
3348  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(6, TrackElement.HLoc + 1, TrackElement.VLoc, 7, RouteNumber))
3349  {
3350  if(AllRoutes->GetFixedRouteAt(213, RouteNumber).PrefDirSize() > 2)
3351  {
3352  // don't call for stub end routes
3354  LogActionErrorCalled = true;
3355  }
3356  AllRoutes->GetModifiableRouteAt(26, RouteNumber).ForceCancelRoute(9);
3357  }
3358  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(7, TrackElement.HLoc, TrackElement.VLoc + 1, 3, RouteNumber))
3359  // not else in case have different routes on each diagonal, though shouldn't be possible
3360  {
3361  if(!LogActionErrorCalled && AllRoutes->GetFixedRouteAt(214, RouteNumber).PrefDirSize() > 2)
3362  {
3363  // don't call for stub end routes
3365  }
3366  AllRoutes->GetModifiableRouteAt(27, RouteNumber).ForceCancelRoute(10);
3367  }
3368  }
3369  }
3370  }
3371  Utilities->CallLogPop(698);
3372  return; // no route on other track or no other track
3373  }
3374  // here if there is a route at Element & EntryPos - so there can't be a route on the other track if a 4 track element, unless it's a bridge & that's ok
3375  for(unsigned int x = 0; x < AllRoutes->GetFixedRouteAt(155, RouteNumber).PrefDirSize(); x++)
3376  {
3377  RouteElement = AllRoutes->GetFixedRouteAt(156, RouteNumber).GetFixedPrefDirElementAt(165, x);
3378  bool PointsAtElement = (Track->TrackElementAt(987, Element).TrackType == Points); // new at v2.4.2 for points check - Xeon repoted it 30/05/20. He found that for routes that
3379  if(RouteElement.GetTrackVectorPosition() == (unsigned int)Element) // cross bridges at both levels can have entrypos 0 & other exitpos 2 so if don't have this check can cancel a route wrongly
3380  {
3381  if(RouteElement.GetELinkPos() == EntryPos)
3382  {
3383  Utilities->CallLogPop(699);
3384  return; // right direction
3385  }
3386  else if((RouteElement.GetELinkPos() == 2) && (EntryPos == 0) && PointsAtElement)
3387  {
3388  Utilities->CallLogPop(700);
3389  return; // right direction (points)
3390  }
3391  else if((RouteElement.GetELinkPos() == 0) && (EntryPos == 2) && PointsAtElement)
3392  {
3393  Utilities->CallLogPop(701);
3394  return; // right direction (points)
3395  }
3396  else if(RouteElement.GetXLinkPos() == EntryPos)
3397  {
3398  WrongRoute = true;
3399  break; // wrong direction
3400  }
3401  else if((RouteElement.GetXLinkPos() == 2) && (EntryPos == 0) && PointsAtElement) // ok for bridges
3402  {
3403  WrongRoute = true;
3404  break; // wrong direction
3405  }
3406  else if((RouteElement.GetXLinkPos() == 0) && (EntryPos == 2) && PointsAtElement) // ok for bridges
3407  {
3408  WrongRoute = true;
3409  break; // wrong direction
3410  }
3411  }
3412  }
3413  if(!WrongRoute)
3414  {
3415  throw Exception("Error, Element in route but no route found in CheckAndCancelRouteForWrongEndEntry");
3416  }
3417  if(AllRoutes->GetFixedRouteAt(180, RouteNumber).PrefDirSize() > 2)
3418  {
3419  // don't call for stub end routes
3421  }
3422  AllRoutes->GetModifiableRouteAt(14, RouteNumber).ForceCancelRoute(2);
3423  Utilities->CallLogPop(703);
3424 }
3425 
3426 // ---------------------------------------------------------------------------
3427 
3428 void TTrain::PlotTrainWithNewBackgroundColour(int Caller, TColor NewBackgroundColour, TDisplay *Disp)
3429 {
3430  if(BackgroundColour == NewBackgroundColour)
3431  {
3432  return; // don't replot if already correct
3433 
3434  }
3435  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PlotTrainWithNewBackgroundColour," + AnsiString(NewBackgroundColour));
3436  bool ColourError = false, ColourError2 = false;
3437 
3438  RailGraphics->ChangeBackgroundColour(1, FrontCodePtr, FrontCodePtr, NewBackgroundColour, BackgroundColour, ColourError);
3439  if(ColourError)
3440  {
3441  ColourError2 = true;
3442  }
3443  for(int x = 0; x < 4; x++)
3444  {
3445  RailGraphics->ChangeBackgroundColour(2, HeadCodeGrPtr[x], HeadCodeGrPtr[x], NewBackgroundColour, BackgroundColour, ColourError);
3446  if(ColourError)
3447  {
3448  ColourError2 = true;
3449  }
3450  }
3451  if(ColourError2)
3452  {
3454  "ERROR: Colour depth insufficient to display train colours properly. Please ensure that the 'safe' (web) palette of 256 colours can be displayed");
3455  }
3456  // NB need a separate 'for' loop since the plot order can be different from the graphic order depending on the direction
3457  // of motion
3458  for(int x = 0; x < 4; x++)
3459  {
3460  PlotTrainGraphic(6, x, Disp);
3461  }
3462  BackgroundColour = NewBackgroundColour;
3463  Display->Update();
3464  // need to keep this since Update() not called for PlotSmallOutput as too slow
3465  Utilities->CallLogPop(704);
3466 }
3467 
3468 // ---------------------------------------------------------------------------
3469 
3470 void TTrain::SetTrainMovementValues(int Caller, int TrackVectorPosition, int EntryPos)
3471 /*
3472 Note: Within the loop BrakeRate can only increase and MaxExitSpeed can only reduce
3473 
3474 Summary: Called during PlotStartPosition to set initial values, when stopped and need to restart, and during UpdateTrain when Straddle is LeadMidLag,
3475 i.e. just as the front of a train is about to move fully onto an element, where TrackVectorPosition is the element immediately in front
3476 of the element the front of the train is moving fully on to. The function calculates the times and speeds at the next half-element and
3477 full-element moves.
3478 
3479 Detail: TrackVectorPosition & EntryPos correspond to the TrackVector element immediately in front of where the train is at
3480 the end of the current Update(). EntrySpeed is needed but this is a class data member so isn't passed in. Set the
3481 train BrakeRate to zero (for now, likely to be altered later), & check if zero entry speed with another train directly in front & if so
3482 remain stopped. Pick up the half length value and speed limit for the EntryPos track, and set FrontElementLength to the length of the
3483 EntryPos track, then set LimitingSpeed to the minimum of the element speed limit or the train's maximum speed. Check if running past a
3484 red signal and set SPADFlag if so (use 1 for EntrySpeed rather than 0 as this value is a double so could be slightly in excess of 0).
3485 In this case set the brake rate to maximum to stop as soon as possible.
3486 
3487 For no SPAD calculate the distance that will be travelled at the maximum speed at which the train can exit the next element at half
3488 MaxBrakeRate, this is DistanceAtHalfBraking (also calculate DistanceAtThreeQuarterBraking - used for stopping under signaller control).
3489 DistanceAtHalfBraking is used as the limiting forward look from the next element (i.e. following EntryPos)
3490 for computing the actual braking rate. If no more restrictive speed limits or reasons to stop are found within the forward look then the
3491 train can accelerate or stay at its (local) maximum speed for the next element. The maximum speed on exit from the next element is used
3492 for calculating the forward look because it represents the worst case - i.e. assumes that the train accelerates for the next element.
3493 
3494 A loop is now entered where the CumulativeLength is updated and each successive element (if there are any - current element checked
3495 first to see whether buffers or continuation) in turn is examined: first the length of the
3496 current element is added to the cumulative length; then the half length and speedlimit are set for the next element - points are
3497 followed according to their current setting (Attribute), but derailments are ignored as these are dealt with outside this function; checks
3498 are then made to see whether the next element is a red signal (train should stop before it); next element is a buffer (train should stop
3499 at the end of it so the cumulative length has the next element length added); current element is a buffer (train should stop
3500 at the end of the current element so no need to alter the cumulative length); or have reached a named location stop position. For any of
3501 these reasons, or if stopping under signaller control, there is no more looping, instead the braking rate is calculated to bring the train
3502 to a stop over CumulativeLength. For all normal purposes the braking rate will be less than half (light braking), or less than three
3503 quarters if stopping under signaller control (heavy braking). However if signals are reset in front of a train then the train may need
3504 emergency braking (> 90% max brake rate) and a SPAD may result. Similarly if points are chaged in front of a train that divert it into a
3505 siding then again emeregency braking may be necessary and a crash may result.
3506 
3507 If the train is due to stop then the function calculates the half and full times and speeds and returns. However the calculation depends
3508 on the conditions at entry. If the EntrySpeed is lower than MaxHalfSpeed and the EntryPos element is the one
3509 that the train has to stop at the end of, as it might be for example if train had been stopped at a signal and the next element is a
3510 buffer, then the train accelerates for half the element and brakes for the other half.
3511 Now the BrakeRate is calculated (limited to the MaxBrakeRate), but if it is less than a value calculated at an earlier pass round
3512 the loop then it retains its earlier value (may be due to a close speed restriction that requires more braking than a more distant stop
3513 requirement). The MaxExitSpeedAtHalfBraking (maximum speed at which the train can leave the current element and still stop when required
3514 at half the max braking rate) value is also calculated using EntrySpeed and CumulativeLength, but limiting it to the line speed limit or
3515 train MaxRunningSpeed whichever is the lower. If EntrySpeed > MaxExitSpeedAtHalfBraking then braking is required, so the half and full
3516 speed and time values for the current element are calculated using BrakeRate, EntrySpeed and CurrentElementHalfLength. If need to stop
3517 at the end of the current elemecumulativent for other than a red signal (SPADs can occur) then ExitSpeedFull is set to 0. It should be calculated
3518 as 0 anyway for other than a red signal but this makes sure. If EntrySpeed <= MaxExitSpeedAtHalfBraking then can calculate the half and
3519 full speed and time values for acceleration over the current element, but limit ExitSpeedHalf & Full to MaxExitSpeedAtHalfBraking or to
3520 the current element speed limit if necessary. Check whether ExitSpeedHalf <= EntrySpeed (+0.01 since it's a double) and use constant speed
3521 time values for Half & Full if so, but prior to this increase EntrySpeed if necessary to avoid a divide by zero error.
3522 
3523 If the train is not due to stop within the DistanceAtHalfBraking from the next element following EntryPos then the next element (if there
3524 is one) is checked to see if its speed limit is less than the current value of LimitingSpeed (which is the minimum of any earlier element's
3525 speed limit that has been examined within the loop and the train's MaxRunning speed), and if so LimitingSpeed is set down to it. Now
3526 the MaxExitSpeedAtHalfBraking is calculated, limiting it to LimitingSpeed if less, in case need to accelerate in the current element, in
3527 which case the exit speeds need to be limited to MaxExitSpeedAtHalfBraking. If EntrySpeed > LimitingSpeed then calculate the braking rate
3528 to bring the speed down to LimitingSpeed in CumulativeLength, keeping the existing BrakeRate value if lower and keeping it within
3529 MaxBrakeRate.
3530 
3531 Then, providing the current element isn't a buffer or continuation, the 'Current' values are updated from the 'Next' values ready for
3532 the next loop iteration. The loop is broken out of if the current element is a buffer or continuation, the next element is a
3533 continuation, or (CumulativeLength - FrontElementLength) >= DistanceAtHalfBraking.
3534 
3535 Now the final Half and Full values can be set for braking (if BrakeRate > 0.01), or accelerating - limiting the half and full exit speed
3536 values to MaxExitSpeedAtHalfBraking if necessary, and using constant speed time values if the exit speeds aren't much different to
3537 EntrySpeed and EntrySpeed > 0.01 (to avoid a divide by zero error).
3538 
3539 Note that in no circumstances will a train stop when straddling 3 elements, it will always be fully on two elements. This is ensured
3540 by UpdateTrain() which never sets any stop conditions unless the train is fully on 2 elements when that function returns, i.e. entered
3541 when Straddle == LeadMidLag
3542 */
3543 {
3544  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SetTrainMovementValues," + AnsiString(TrackVectorPosition) + "," +
3545  AnsiString(EntryPos) + "," + HeadCode);
3546  int EntryHalfLength, CurrentElementHalfLength, NextElementHalfLength, CumulativeLength = 0, CurrentTrackVectorPosition = TrackVectorPosition;
3547  int DistanceAtHalfBraking, DistanceAtThreeQuarterBraking, ExitPos, NextTrackVectorPosition, NextEntryPos;
3548  bool RedSignalFlag = false, BuffersFlag = false, StationFlag = false, BuffersOrContinuationNowFlag = false, ContinuationNextFlag = false,
3549  TrainInFrontInSignallerModeFlag = false;
3550  double LimitingSpeed, FrontElementMaxSpeed, MaxExitSpeedAtHalfBrakingSquared, MaxExitSpeedAtHalfBraking, NextSpeedLimit, TempBrakeRate;
3551  double ExitSpeedHalfSquared, ExitSpeedFullSquared;
3552  bool SignallerStopRequired = false;
3553 
3555  // set high to begin with to avoid divide by zero errors on restart after stops, will be set lower later
3556 
3557  // Member variables: EntrySpeed, ExitSpeedHalf, ExitSpeedFull, MaxExitSpeed, BrakeRate, EntryTime, ExitTimeHalf, ExitTimeFull, FrontElementSpeedLimit, FrontElementLength;
3558 
3559  OneLengthAccelDecel = false;
3560  BrakeRate = 0;
3561 
3562 //find FrontElementLength & FrontElementSpeedLimit (these correspond to TrackVectorPosition input value);
3563  if(CurrentTrackVectorPosition > -1)
3564  {
3565  if(Track->TrackElementAt(855, CurrentTrackVectorPosition).TrackType == Points) // this test & section added at v0.6
3566  {
3567  if((EntryPos == 0) || (EntryPos == 2))
3568  {
3569  if(Track->TrackElementAt(856, CurrentTrackVectorPosition).Attribute == 0)
3570  {
3571  CurrentElementHalfLength = (Track->TrackElementAt(857, CurrentTrackVectorPosition).Length01) / 2;
3572  FrontElementSpeedLimit = Track->TrackElementAt(858, CurrentTrackVectorPosition).SpeedLimit01;
3573  }
3574  else
3575  {
3576  CurrentElementHalfLength = (Track->TrackElementAt(859, CurrentTrackVectorPosition).Length23) / 2;
3577  FrontElementSpeedLimit = Track->TrackElementAt(860, CurrentTrackVectorPosition).SpeedLimit23;
3578  }
3579  }
3580  else if(EntryPos == 1)
3581  {
3582  CurrentElementHalfLength = (Track->TrackElementAt(861, CurrentTrackVectorPosition).Length01) / 2;
3583  FrontElementSpeedLimit = Track->TrackElementAt(862, CurrentTrackVectorPosition).SpeedLimit01;
3584  }
3585  else // == 3
3586  {
3587  CurrentElementHalfLength = (Track->TrackElementAt(863, CurrentTrackVectorPosition).Length23) / 2;
3588  FrontElementSpeedLimit = Track->TrackElementAt(864, CurrentTrackVectorPosition).SpeedLimit23;
3589  }
3590  }
3591  else
3592  {
3593  if(EntryPos > 1)
3594  {
3595  CurrentElementHalfLength = (Track->TrackElementAt(348, CurrentTrackVectorPosition).Length23) / 2;
3596  FrontElementSpeedLimit = Track->TrackElementAt(349, CurrentTrackVectorPosition).SpeedLimit23;
3597  }
3598  else
3599  {
3600  CurrentElementHalfLength = (Track->TrackElementAt(350, CurrentTrackVectorPosition).Length01) / 2;
3601  FrontElementSpeedLimit = Track->TrackElementAt(351, CurrentTrackVectorPosition).SpeedLimit01;
3602  }
3603  }
3604  EntryHalfLength = CurrentElementHalfLength;
3605  FrontElementLength = 2 * CurrentElementHalfLength;
3606  }
3607  else
3608  {
3609  throw Exception("Error - CurrentTrackVectorPosition < 0 in SetTrainMovementValues");
3610  }
3611  if((CurrentElementHalfLength < 0) || (FrontElementSpeedLimit < 0))
3612  {
3613  throw Exception("Error - HalfLength or SpeedLimit < 0 in SetTrainMovementValues");
3614  }
3615  // check if zero entry speed with another train directly in front & if so remain stopped
3616  if(Track->OtherTrainOnTrack(2, CurrentTrackVectorPosition, EntryPos, TrainID) && (EntrySpeed < 1))
3617  {
3618  EntrySpeed = 0;
3619  ExitSpeedHalf = 0;
3620  ExitSpeedFull = 0;
3621  MaxExitSpeed = 0;
3622  BrakeRate = 0;
3623  ExitTimeHalf = EntryTime + TDateTime(1/24); //set this high in case used later though unlikely
3624  ExitTimeFull = EntryTime + TDateTime(1/23); //set about 2.5 mins later than half time
3625  StoppedForTrainInFront = true;
3626  Utilities->CallLogPop(705);
3627  return;
3628  }
3629  // new at v2.4.0 - check for stopped and zero power
3630  if((EntrySpeed < 1) && PowerAtRail < 1)
3631  {
3632  EntrySpeed = 0;
3633  ExitSpeedHalf = 0;
3634  ExitSpeedFull = 0;
3635  MaxExitSpeed = 0;
3636  BrakeRate = 0;
3637  ExitTimeHalf = EntryTime + TDateTime(1/24); //set this high in case used later though unlikely
3638  ExitTimeFull = EntryTime + TDateTime(1/23); //set about 2.5 mins later than half time
3639  StoppedWithoutPower = true;
3640  Utilities->CallLogPop(2125);
3641  return;
3642  }
3643 //set LimitingSpeed & FrontElementMaxSpeed (internal values)
3644  if(BeingCalledOn)
3645  {
3646  LimitingSpeed = CallOnMaxSpeed;
3647  }
3648  else
3649  {
3650  LimitingSpeed = MaximumSpeedLimit;
3651  }
3652  if(LimitingSpeed > FrontElementSpeedLimit)
3653  {
3654  LimitingSpeed = FrontElementSpeedLimit;
3655  }
3656  if(LimitingSpeed > MaxRunningSpeed) //MaxRunningSpeed is set in AddTrain depending on timetable or signaller control mode
3657  {
3658  LimitingSpeed = MaxRunningSpeed;
3659  }
3660  FrontElementMaxSpeed = LimitingSpeed;
3661 
3662 /*
3663  for braking the deceleration rate is constant so the following formuli (Newton's Laws) are used:-
3664  (1) V^2/(3.6^2) = U^2/(3.6^2) - 2FS;
3665  (2) V/3.6 = U/3.6 - FT;
3666  (3) S = UT/3.6 - 0.5FT^2
3667  where(V = final speed in kph [km/h/3.6 = m/s], U = initial speed in km/h, F = deceleration rate in m/s/s, S = distance in m & T = time in secs)
3668 
3669  for accelerating the energy input rate (PowerAtRail) is constant so the following formuli are used:-
3670  (4) V^2/(3.6^2) - U^2/(3.6^2) = A^2T;
3671  (5) V = 3.6 * ((1.5*S*A^2) + U^3/ (3.6)^3)^0.333334;
3672  where A is a constant (2*PowerAtRail/Mass)^0.5; V = final speed in kph, U = initial speed in kph , S = distance in m & T = time in secs
3673  It's a bit unrealistic during the early acceleration phase as it will be too rapid, but shouldn't affect the running unduly
3674 
3675  calc max speed that can attain on exit from next element (as could accelerate over next element) and use that speed to calc
3676  DistanceAtHalfBraking, if use actual speed may miss a stop requirement just outside look-ahead & accelerate, and at next calc
3677  be unable to stop or have hard acceleration followed immediately by hard braking, this speed makes for smoother operation
3678 */
3679 
3680 // check if running past a red signal without permission
3681  if((Track->TrackElementAt(352, CurrentTrackVectorPosition).Config[Track->GetNonPointsOppositeLinkPos(EntryPos)] == Signal) && (Track->TrackElementAt(353,
3682  CurrentTrackVectorPosition).Attribute == 0) && (EntrySpeed > 1) && !AllowedToPassRedSignal)
3683  {
3684  SPADFlag = true; // user has to intervene to reset & restart after spad
3685  }
3686  if(!SPADFlag)
3687  {
3688  ExitSpeedFull = 3.6 * Power(((3 * EntryHalfLength * AValue * AValue) + (EntrySpeed * EntrySpeed * EntrySpeed / 3.6 / 3.6 / 3.6)), 0.333334);
3689  // to begin with calc the maximum exit speed (assumes accelerating) and then reduce it if necessary
3690  // for accelerating the energy input rate (PowerAtRail) is constant so the following formuli are used:-
3691  // (1) V^2/(3.6^2) - U^2/(3.6^2) = A^2T; (2) V = 3.6 * ((1.5*S*A^2) + U^3/(3.6^3))^0.333334;
3692  // where A is a constant (2*PowerAtRail/Mass)^0.5; V = final speed in kph, U = initial speed in kph, S = distance & T = time, note that km/h/3.6 = m/s
3693  // This is a bit unrealistic during the early acceleration phase as it will be too rapid, but shouldn't affect the running unduly
3694 
3695  double ExitSpeedAtMaxBraking;
3696  // below introduced at v2.4.0, was ExitSpeedFull = LimitingSpeed; but that allowed very high brake rates when
3697  // took signaller control of a fast failed train with signaller limiting speed 30km/h
3699  {
3700  ExitSpeedAtMaxBraking = 0;
3701  }
3702  else
3703  {
3704  ExitSpeedAtMaxBraking = sqrt((EntrySpeed * EntrySpeed) - 2 * MaxBrakeRate * FrontElementLength);
3705  }
3706  double SpeedToUse;
3707  // use the highest of LimitingSpeed or ExitSpeedAtMaxBraking - added at v2.4.2 because trains entering at a continuation with zero (or very low) speed
3708  // & 2 elements before signal caused ExitSpeedAtMaxBraking & hence DistanceAtHalfBraking and DistanceAtThreeQuarterBraking to be zero, so no restriction was recognised
3709  // for first element & train accelerated at maximum rate, then at 2nd element train couldn't brake in time and overran the signal - notified by Micke via Discord on 02/06/20
3710  if(ExitSpeedAtMaxBraking > LimitingSpeed)
3711  {
3712  SpeedToUse = ExitSpeedAtMaxBraking;
3713  }
3714  else
3715  {
3716  SpeedToUse = LimitingSpeed;
3717  }
3718  if(ExitSpeedFull > SpeedToUse)
3719  {
3720  ExitSpeedFull = SpeedToUse;
3721  }
3722  DistanceAtHalfBraking = ExitSpeedFull * ExitSpeedFull / 3.6 / 3.6 / MaxBrakeRate;
3723  DistanceAtThreeQuarterBraking = ExitSpeedFull * ExitSpeedFull / 3.6 / 3.6 / 1.5 / MaxBrakeRate; // used for signaller stops
3724 
3725  //now enter a do loop to examine each element in turn from the front of the train to calc the cumulative length and to see if a stop is required (flag set if so -
3726  //RedSignalFlag, BuffersFlag, StationFlag, TrainInFrontInSignallerModeFlag, SignallerStopRequired, StepForwardFlag) in which case there are no more loops
3727  // break out of the loop when ((CumulativeLength - FrontElementLength) < DistanceAtHalfBraking ) && ((!BuffersOrContinuationNowFlag && !ContinuationNextFlag) || SignallerStoppingFlag);
3728 
3729  do
3730  {
3731  RedSignalFlag = false;
3732  BuffersFlag = false;
3733  StationFlag = false;
3734  BuffersOrContinuationNowFlag = false;
3735  ContinuationNextFlag = false;
3736  // have to reset this after the above test
3737  // add current element length to CumulativeLength
3738  CumulativeLength += (2 * CurrentElementHalfLength);
3739  if((CumulativeLength >= DistanceAtThreeQuarterBraking) && (TrainMode == Signaller) && SignallerStoppingFlag)
3740  {
3741  SignallerStopRequired = true;
3742  // once set stays set until SignallerStoppingFlag reset, providing !BuffersOrContinuationNowFlag,
3743  // set SignallerStopBrakeRate to stop in CumulativeLength unless already higher (i.e. can only increase)
3744  double TempBR = EntrySpeed * EntrySpeed / 2 / 3.6 / 3.6 / CumulativeLength;
3745  if(SignallerStopBrakeRate < TempBR)
3746  {
3747  SignallerStopBrakeRate = TempBR;
3748  }
3749  }
3750  // first check for stops within the length of the current element, where don't want any more checks & don't want
3751  // to add in any extra to the CumulativeLength. Only applies for buffers & station stops as signals should have been caught
3752  // during the last loop when the NextTrackVectorPosition was the signal.
3753 
3754  // check if current element is a buffer
3755  if(Track->TrackElementAt(374, CurrentTrackVectorPosition).TrackType == Buffers)
3756  {
3757  // no need to add in the length of this element to CumulativeLength as already included
3758  BuffersFlag = true;
3759  }
3760  // check if current element is a station stop
3761  if(TrainMode == Timetable)
3762  {
3763  bool StopRequired = false;
3764  if(!TimetableFinished && (NameInTimetableBeforeCDT(12, Track->TrackElementAt(375, CurrentTrackVectorPosition).ActiveTrackElementName,
3765  StopRequired) > -1) && ((Track->TrackElementAt(376, CurrentTrackVectorPosition).StationEntryStopLinkPos1 == EntryPos) ||
3766  (Track->TrackElementAt(377, CurrentTrackVectorPosition).StationEntryStopLinkPos2 == EntryPos)))
3767  {
3768  // no need to add in the length of element to CumulativeLength
3769  if(StopRequired)
3770  {
3771  StationFlag = true;
3772  }
3773  }
3774  }
3775  else
3776  {
3777  StationFlag = false;
3778  }
3779  // set NextHalfLength & NextSpeedLimit, but only if current element not buffers or exit continuation - no next element for them
3780  if(((Track->TrackElementAt(354, CurrentTrackVectorPosition).TrackType == Buffers) || (Track->TrackElementAt(355,
3781  CurrentTrackVectorPosition).TrackType == Continuation)) && (EntryPos == 1))
3782  {
3783  BuffersOrContinuationNowFlag = true;
3784  }
3785  if(!BuffersOrContinuationNowFlag && !BuffersFlag && !StationFlag) // skip if buffers or station flags already set
3786  {
3787  if(Track->TrackElementAt(356, CurrentTrackVectorPosition).TrackType == Points)
3788  {
3789  if((EntryPos == 0) || (EntryPos == 2))
3790  {
3791  if(Track->TrackElementAt(357, CurrentTrackVectorPosition).Attribute == 0)
3792  {
3793  ExitPos = 1;
3794  }
3795  else
3796  {
3797  ExitPos = 3;
3798  }
3799  }
3800  else
3801  {
3802  ExitPos = 0;
3803  }
3804  }
3805  else
3806  {
3807  ExitPos = Track->GetNonPointsOppositeLinkPos(EntryPos);
3808  }
3809  NextTrackVectorPosition = Track->TrackElementAt(358, CurrentTrackVectorPosition).Conn[ExitPos];
3810  NextEntryPos = Track->TrackElementAt(359, CurrentTrackVectorPosition).ConnLinkPos[ExitPos];
3811  if(NextTrackVectorPosition > -1)
3812  {
3813  if(Track->TrackElementAt(845, NextTrackVectorPosition).TrackType == Points)
3814  // this test & section added at v0.6
3815  {
3816  if((NextEntryPos == 0) || (NextEntryPos == 2))
3817  {
3818  if(Track->TrackElementAt(846, NextTrackVectorPosition).Attribute == 0)
3819  {
3820  NextElementHalfLength = (Track->TrackElementAt(847, NextTrackVectorPosition).Length01) / 2;
3821  NextSpeedLimit = Track->TrackElementAt(848, NextTrackVectorPosition).SpeedLimit01;
3822  }
3823  else
3824  {
3825  NextElementHalfLength = (Track->TrackElementAt(849, NextTrackVectorPosition).Length23) / 2;
3826  NextSpeedLimit = Track->TrackElementAt(850, NextTrackVectorPosition).SpeedLimit23;
3827  }
3828  }
3829  else if(NextEntryPos == 1)
3830  {
3831  NextElementHalfLength = (Track->TrackElementAt(851, NextTrackVectorPosition).Length01) / 2;
3832  NextSpeedLimit = Track->TrackElementAt(852, NextTrackVectorPosition).SpeedLimit01;
3833  }
3834  else // == 3
3835  {
3836  NextElementHalfLength = (Track->TrackElementAt(853, NextTrackVectorPosition).Length23) / 2;
3837  NextSpeedLimit = Track->TrackElementAt(854, NextTrackVectorPosition).SpeedLimit23;
3838  }
3839  }
3840  else
3841  {
3842  if(NextEntryPos > 1)
3843  {
3844  NextElementHalfLength = (Track->TrackElementAt(360, NextTrackVectorPosition).Length23) / 2;
3845  NextSpeedLimit = Track->TrackElementAt(361, NextTrackVectorPosition).SpeedLimit23;
3846  }
3847  else
3848  {
3849  NextElementHalfLength = (Track->TrackElementAt(362, NextTrackVectorPosition).Length01) / 2;
3850  NextSpeedLimit = Track->TrackElementAt(363, NextTrackVectorPosition).SpeedLimit01;
3851  }
3852  }
3853  }
3854  else
3855  {
3856  throw Exception("Error - Trying to access NextTrackVectorPosition when none present in SetTrainMovementValues");
3857  }
3858  // now check for stops, first cover those where don't want to add in length of next element
3859  // check if next element is a red signal - Attr 0,
3860  // note that this doesn't apply to trains stopped at a red signal since the signal position is
3861  // CurrentTrackVectorPosition not NextTrackVectorPosition
3862  bool StopRequired;
3863  if(Track->TrackElementAt(364, NextTrackVectorPosition).Config[Track->GetNonPointsOppositeLinkPos(NextEntryPos)] == Signal)
3864  {
3865  if(Track->TrackElementAt(365, NextTrackVectorPosition).Attribute == 0)
3866  {
3867  // no need to add in the length of element to CumulativeLength
3868  RedSignalFlag = true;
3869  }
3870  // next element is a red signal
3871  }
3872  // check if current element is a station & next element contains a train - trains will always stop without crashing at a
3873  // station they are due to stop at even if there is a train in front blocking the normal stop position - providing there is
3874  // at least one platform element free
3876  CurrentTrackVectorPosition).ActiveTrackElementName, StopRequired) > -1) && Track->OtherTrainOnTrack(3, NextTrackVectorPosition,
3877  NextEntryPos, TrainID))
3878  {
3879  // no need to add in the length of element to CumulativeLength
3880  if(StopRequired)
3881  {
3882  StationFlag = true;
3883  }
3884  }
3885  // check if next element contains a train & in Signaller mode (always stops for train in front if in signaller mode)
3886  else if((TrainMode == Signaller) && Track->OtherTrainOnTrack(4, NextTrackVectorPosition, NextEntryPos, TrainID))
3887  // (Track->TrackElementAt(651, NextTrackVectorPosition).TrainIDOnElement > -1))
3888  {
3889  // no need to add in the length of element to CumulativeLength
3890  TrainInFrontInSignallerModeFlag = true;
3891  }
3892  // check if next element is a buffer
3893  else if(Track->TrackElementAt(366, NextTrackVectorPosition).TrackType == Buffers)
3894  {
3895  // need to add in the length of that element to CumulativeLength
3896  CumulativeLength += Track->TrackElementAt(367, NextTrackVectorPosition).Length01;
3897  BuffersFlag = true;
3898  }
3899  // check if next element is a station stop
3901  NextTrackVectorPosition).ActiveTrackElementName, StopRequired) > -1) && ((Track->TrackElementAt(371,
3902  NextTrackVectorPosition).StationEntryStopLinkPos1 == EntryPos) || (Track->TrackElementAt(372,
3903  NextTrackVectorPosition).StationEntryStopLinkPos2 == EntryPos)))
3904  {
3905  // need to add in the length of that element to CumulativeLength if a stop required
3906  if(StopRequired)
3907  {
3908  StationFlag = true;
3909  CumulativeLength += Track->TrackElementAt(373, NextTrackVectorPosition).Length01;
3910  }
3911  }
3912  }
3913  //now can decide whether need to stop over CumulativeLength
3914  if(RedSignalFlag || BuffersFlag || StationFlag || TrainInFrontInSignallerModeFlag || SignallerStopRequired || StepForwardFlag) // no more loops
3915  {
3916  // have to come to a stop over CumulativeLength
3917  if(CumulativeLength == FrontElementLength)
3918  // will be if StepForwardFlag (if stopped to begin with on zero power then earlier check will intercept it and it won't reach here
3919  // only one length to go before stop so check whether need to accelerate for half length then brake for latter
3920  // half; calc speed at halfway point that corresponds to half braking rate for latter half of track element,
3921  // and if less than EntrySpeed then skip this section (don't need any acceleration)
3922  // if not calc speed at halfway point & if less than above set half speed to this value;
3923  // use constant acceleration in calculating half time point
3924  {
3925  MaxExitSpeed = 0;
3926  double MaxHalfSpeed;
3927  double MaxHalfSpeedAtHalfBraking = 3.6 * sqrt(MaxBrakeRate * FrontElementLength / 2);
3928  // have to halve the element length, & can't be zero or negative so no need to test
3929  // if(MaxHalfSpeedAtHalfBraking > LimitingSpeed) MaxHalfSpeed = LimitingSpeed; else MaxHalfSpeed = MaxHalfSpeedAtHalfBraking;
3930  if(MaxHalfSpeedAtHalfBraking > FrontElementMaxSpeed)
3931  {
3932  MaxHalfSpeed = FrontElementMaxSpeed;
3933  }
3934  else
3935  {
3936  MaxHalfSpeed = MaxHalfSpeedAtHalfBraking;
3937  }
3938  if(MaxHalfSpeed > (2 * EntrySpeed))
3939  // use 2x to prevent kangarooing at last element when had
3940  // been braking smoothly at less that 50% braking rate, 2x should prevent all but extreme cases
3941  {
3942  ExitSpeedHalf = 3.6 * Power(((1.5 * EntryHalfLength * AValue * AValue) + (EntrySpeed * EntrySpeed * EntrySpeed / 3.6 / 3.6 / 3.6)),
3943  0.333334);
3944  bool HalfSpeedLimited = false;
3945  if(MaxHalfSpeed < ExitSpeedHalf)
3946  {
3947  ExitSpeedHalf = MaxHalfSpeed;
3948  HalfSpeedLimited = true;
3949  }
3950  if(PowerAtRail > 1)
3951  // added at v2.4.0 in case reach here with failed train, when can't use AValue in denominator as close zero
3952  {
3953  // [km/h/3.6 = m/s]
3954  ExitTimeHalf =
3955  EntryTime + TDateTime(((ExitSpeedHalf * ExitSpeedHalf) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue) / 86400.0);
3956  }
3957  else
3958  {
3959  ExitTimeHalf = EntryTime + TDateTime(EntryHalfLength * 3.6 / EntrySpeed / 86400.0);
3960  }
3961  // the above is the time taken to accelerate to ExitSpeedHalf, so if this is reached before the half
3962  // way point (i.e. HalfSpeedLimited is set) then the time will be too short; change later to equal the
3963  // braking time; not fully accurate but better to be equal than have a short acceleration period followed
3964  // by a long braking period
3965  ExitSpeedFull = 0;
3966  TempBrakeRate = ExitSpeedHalf * ExitSpeedHalf / 2 / 3.6 / 3.6 / EntryHalfLength;
3967  if(TempBrakeRate > MaxBrakeRate)
3968  {
3969  TempBrakeRate = MaxBrakeRate;
3970  }
3971  // shouldn't be but leave in anyway
3972  if(TempBrakeRate > BrakeRate)
3973  {
3974  BrakeRate = TempBrakeRate;
3975  }
3976  // BrakeRate may already have been set in an earlier loop so don't want to reduce it
3977  ExitTimeFull = ExitTimeHalf + TDateTime(ExitSpeedHalf / 3.6 / BrakeRate / 86400.0);
3978  if(HalfSpeedLimited)
3979  // this is the change referred to above
3980  {
3981  TDateTime BrakingTime = ExitTimeFull - ExitTimeHalf;
3982  ExitTimeHalf = EntryTime + BrakingTime;
3983  ExitTimeFull = ExitTimeHalf + BrakingTime;
3984  }
3985  OneLengthAccelDecel = true; //used in TrackTrainFloat in InterfaceUnit.cpp to show accelerating for first half move then decelerating
3986  Utilities->CallLogPop(1095);
3987  return;
3988  }
3989  }
3990  // set braking to achieve speed = 0 @ CumulativeLength up to MaxBrakeRate
3991  // calc MaxExitSpeed for element at EntryPosition & set to this or existing val if lower,
3992  // calc th, tf, sh, & sf
3993  TempBrakeRate = EntrySpeed * EntrySpeed / 2 / 3.6 / 3.6 / CumulativeLength;
3994  if(TempBrakeRate > MaxBrakeRate)
3995  {
3996  TempBrakeRate = MaxBrakeRate;
3997  }
3998  if(TempBrakeRate > BrakeRate)
3999  {
4000  BrakeRate = TempBrakeRate;
4001  }
4002  // BrakeRate may already have been set in an earlier loop so don't want to reduce it
4003  if(SignallerStopRequired)
4004  // set BrakeRate to max of its calculated value or SignallerStopBrakeRate
4005  {
4007  {
4009  // this prevents the brakerate from reducing for a signaller stop
4010  // regardless of other conditions that may change as progress round the loop
4011  }
4012  }
4014  // prevents BrakeRate dropping below SignallerStopBrakeRate once it's been set whether or not SignallerStopRequired set
4015  // SignallerStopRequired may not be set if a red signal found in a later calc, & brakerate may then drop
4016  {
4018  }
4019  int TempMaxExitSpeed;
4020  // calc current value & if less than MaxExitSpeed set that to this
4021  MaxExitSpeedAtHalfBrakingSquared = 3.6 * 3.6 * MaxBrakeRate * (CumulativeLength - FrontElementLength);
4022  if(MaxExitSpeedAtHalfBrakingSquared < 10)
4023  {
4024  MaxExitSpeedAtHalfBraking = 0;
4025  }
4026  else
4027  {
4028  MaxExitSpeedAtHalfBraking = sqrt(MaxExitSpeedAtHalfBrakingSquared);
4029  }
4030  // if(MaxExitSpeedAtHalfBraking > LimitingSpeed) MaxExitSpeed = LimitingSpeed; else MaxExitSpeed = MaxExitSpeedAtHalfBraking;
4031  // I think the above was dropped because it could cause MaxExitSpeed to increase (MaxExitSpeed is an external variable retained between loops)
4032  if(MaxExitSpeedAtHalfBraking > FrontElementMaxSpeed)
4033  {
4034  TempMaxExitSpeed = FrontElementMaxSpeed;
4035  }
4036  else
4037  {
4038  TempMaxExitSpeed = MaxExitSpeedAtHalfBraking;
4039  }
4040  if(TempMaxExitSpeed < MaxExitSpeed)
4041  {
4042  MaxExitSpeed = TempMaxExitSpeed;
4043  }
4044  // here have EntrySpeed & MaxExitSpeed (for the next element), BrakeRate (to bring speed to zero over
4045  // Cumulativelength, and Cumulativelength
4046 
4047  if((EntrySpeed > MaxExitSpeed) || SignallerStopRequired || (SignallerStopBrakeRate > 0.01)) // need to brake
4048  {
4049  ExitSpeedHalfSquared = (EntrySpeed * EntrySpeed) - (3.6 * 3.6 * 2 * BrakeRate * EntryHalfLength);
4050  if(ExitSpeedHalfSquared < 10)
4051  {
4052  ExitSpeedHalf = 0;
4053  }
4054  else
4055  {
4056  ExitSpeedHalf = sqrt(ExitSpeedHalfSquared);
4057  }
4058  ExitTimeHalf = EntryTime + TDateTime((EntrySpeed - ExitSpeedHalf) / 3.6 / BrakeRate / 86400.0);
4059  ExitSpeedFullSquared = (EntrySpeed * EntrySpeed) - (3.6 * 3.6 * 4 * BrakeRate * EntryHalfLength);
4060  if(ExitSpeedFullSquared < 10)
4061  {
4062  ExitSpeedFull = 0;
4063  }
4064  else
4065  {
4066  ExitSpeedFull = sqrt(ExitSpeedFullSquared);
4067  }
4068  if((StationFlag) && (CumulativeLength == FrontElementLength))
4069  {
4070  ExitSpeedFull = 0;
4071  // force a stop for station (not for buffers or red signal)
4072  }
4073  ExitTimeFull = EntryTime + TDateTime((EntrySpeed - ExitSpeedFull) / 3.6 / BrakeRate / 86400.0);
4074  }
4075  // new condition at v2.4.0
4076  else if(PowerAtRail <= 1)
4077  // use EntrySpeed, CumulativeLength & BrakeRate to calculate the half and full exit times and speeds for next element
4078  // avoid using AValue in denominator or have excessively long times
4079  {
4080  ExitSpeedHalfSquared = (EntrySpeed * EntrySpeed) - (3.6 * 3.6 * BrakeRate * FrontElementLength);
4081  ExitSpeedHalf = sqrt(ExitSpeedHalfSquared);
4082  ExitTimeHalf = EntryTime + TDateTime((EntrySpeed - ExitSpeedHalf) / (3.6 * BrakeRate * 86400.0));
4083 
4084  ExitSpeedFullSquared = (EntrySpeed * EntrySpeed) - (3.6 * 3.6 * 2 * BrakeRate * FrontElementLength);
4085  ExitSpeedFull = sqrt(ExitSpeedFullSquared);
4086  ExitTimeFull = EntryTime + TDateTime((EntrySpeed - ExitSpeedFull) / (3.6 * BrakeRate * 86400.0));
4087  }
4088  else // e.g. moving towards a signal or station after a speed limit, so can accelerate unless no power
4089  // without the power need above condition or have hours of delay times, above added at v2.4.0
4090  {
4091  // for accelerating the energy input rate (PowerAtRail) is constant so the following formuli are used:-
4092  // (1) V^2/(3.6^2) - U^2/(3.6^2) = A^2T; (2) V = 3.6 * ((1.5*S*A^2) + U^3/(3.6^3))^0.333334;
4093  // where A is a constant (2*PowerAtRail/Mass)^0.5; V = final speed in kph, U = initial speed in kph, S = distance & T = time, note that km/h/3.6 = m/s
4094  // This is a bit unrealistic during the early acceleration phase as it will be too rapid, but shouldn't affect the running unduly
4095  BrakeRate = 0;
4096  ExitSpeedHalf = 3.6 * Power(((1.5 * EntryHalfLength * AValue * AValue) + (EntrySpeed * EntrySpeed * EntrySpeed / 3.6 / 3.6 / 3.6)),
4097  0.333334);
4098  ExitSpeedFull = 3.6 * Power(((3 * EntryHalfLength * AValue * AValue) + (EntrySpeed * EntrySpeed * EntrySpeed / 3.6 / 3.6 / 3.6)), 0.333334);
4099  // above valid for ExitSpeedHalf & Full <= MaxExitSpeed
4101  // can accelerate continually over the half length
4102  {
4103  ExitTimeHalf = EntryTime + TDateTime(((ExitSpeedHalf * ExitSpeedHalf) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue)
4104  / 86400.0);
4106  // can accelerate continually over the full length
4107  // i.e both (ExitSpeedHalf <= MaxExitSpeed) & (ExitSpeedFull <= MaxExitSpeed)
4108  {
4109  ExitTimeFull =
4110  EntryTime + TDateTime(((ExitSpeedFull * ExitSpeedFull) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue) / 86400.0);
4111  }
4112  else // (ExitSpeedHalf <= MaxExitSpeed) but (ExitSpeedFull > MaxExitSpeed)
4113  // accelerate to MaxExitSpeed then reamin at this speed for rest of element
4114  {
4115  // added at v0.6 as a safeguard
4116  if(MaxExitSpeed < EntrySpeed)
4117  {
4119  }
4120  // to prevent DeltaExitTimeToMaxInSecs being negative
4121  if(MaxExitSpeed < 1)
4122  {
4123  MaxExitSpeed = 1;
4124  }
4125  // to prevent divide by zero error
4126  // below as was
4128  double DeltaExitTimeToMaxInSecs = ((MaxExitSpeed * MaxExitSpeed) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue);
4129  double DistanceToMax = ((MaxExitSpeed * MaxExitSpeed * MaxExitSpeed) - (EntrySpeed * EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / 3.6 /
4130  (1.5 * AValue * AValue);
4131  double RemainingDistance = double(FrontElementLength) - DistanceToMax;
4132  double DeltaRemainingTimeInSecs = 3.6 * RemainingDistance / MaxExitSpeed;
4133  ExitTimeFull = EntryTime + TDateTime((DeltaExitTimeToMaxInSecs + DeltaRemainingTimeInSecs) / 86400.0);
4134  }
4135  }
4136  else // ExitSpeedHalf > MaxExitSpeed, so ExitSpeedFull must also be > MaxExitSpeed
4137  // accelerate over first half to MaxExitSpeed then remain at this speed for rest of the first and
4138  // second halves of the element
4139  {
4140  // added at v0.6 as a safeguard
4141  if(MaxExitSpeed < EntrySpeed)
4142  {
4144  }
4145  // to prevent DeltaExitTimeToMaxInSecs being negative
4146  if(MaxExitSpeed < 1)
4147  {
4148  MaxExitSpeed = 1; // to prevent divide by zero error
4149  }
4150  // below as was
4152  double DeltaExitTimeToMaxInSecs = ((MaxExitSpeed * MaxExitSpeed) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue);
4153  double DistanceToMax = ((MaxExitSpeed * MaxExitSpeed * MaxExitSpeed) - (EntrySpeed * EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / 3.6 /
4154  (1.5 * AValue * AValue);
4155  double RemainingDistance = double(FrontElementLength / 2) - DistanceToMax;
4156  // remaining distance to half length
4157  double DeltaRemainingTimeInSecs = 3.6 * RemainingDistance / MaxExitSpeed;
4158  ExitTimeHalf = EntryTime + TDateTime((DeltaExitTimeToMaxInSecs + DeltaRemainingTimeInSecs) / 86400.0);
4160  ExitTimeFull = ExitTimeHalf + TDateTime(3.6 * EntryHalfLength / MaxExitSpeed / 86400.0);
4161  }
4162  }
4163  Utilities->CallLogPop(706);
4164  return;
4165  }
4166  else
4167  {
4168  if(!BuffersOrContinuationNowFlag)
4169  {
4170  if(NextSpeedLimit < LimitingSpeed)
4171  {
4172  LimitingSpeed = NextSpeedLimit;
4173  }
4174  }
4175  // calc max exit speed at half braking to ensure don't accelerate past it (if acceleration is required)
4176  int TempMaxExitSpeed;
4177  // calc current value & if less than MaxExitSpeed set that to this
4178  // Note that LimitingSpeed is the max value at the end of CumulativeLength, so MaxExitSpeedAtHalfBrakingSquared will be larger
4179  MaxExitSpeedAtHalfBrakingSquared = (LimitingSpeed * LimitingSpeed) + (3.6 * 3.6 * MaxBrakeRate * (CumulativeLength - FrontElementLength));
4180  if(MaxExitSpeedAtHalfBrakingSquared < 10)
4181  {
4182  MaxExitSpeedAtHalfBraking = 0;
4183  }
4184  else
4185  {
4186  MaxExitSpeedAtHalfBraking = sqrt(MaxExitSpeedAtHalfBrakingSquared);
4187  }
4188  if(MaxExitSpeedAtHalfBraking > FrontElementMaxSpeed)
4189  {
4190  TempMaxExitSpeed = FrontElementMaxSpeed;
4191  }
4192  else
4193  {
4194  TempMaxExitSpeed = MaxExitSpeedAtHalfBraking;
4195  }
4196  if(TempMaxExitSpeed < MaxExitSpeed)
4197  {
4198  MaxExitSpeed = TempMaxExitSpeed;
4199  }
4200  // MaxExitSpeed is an external variable & this can reduce its value
4201  if(EntrySpeed > LimitingSpeed)
4202  // note that LimitingSpeed is more restrictive than MaxExitSpeed
4203  // calc TempBrakeRate & set BrakeRate to this or keep existing val if higher
4204  {
4205  TempBrakeRate = ((EntrySpeed * EntrySpeed) - (LimitingSpeed * LimitingSpeed)) / 3.6 / 3.6 / 2 / CumulativeLength;
4206  if(TempBrakeRate > MaxBrakeRate)
4207  {
4208  TempBrakeRate = MaxBrakeRate;
4209  }
4210  // shouldn't be for speedlimits since all known in advance, but include anyway
4211  if(TempBrakeRate > BrakeRate)
4212  {
4213  BrakeRate = TempBrakeRate;
4214  }
4215  // BrakeRate may already have been set in an earlier loop so don't want to reduce it
4216  }
4217  }
4218  if(!BuffersOrContinuationNowFlag)
4219  {
4220  CurrentTrackVectorPosition = NextTrackVectorPosition;
4221  EntryPos = NextEntryPos;
4222  CurrentElementHalfLength = NextElementHalfLength;
4223  if(Track->TrackElementAt(378, NextTrackVectorPosition).TrackType == Continuation)
4224  {
4225  ContinuationNextFlag = true;
4226  }
4227  }
4228  }
4229  while(((CumulativeLength - FrontElementLength) < DistanceAtHalfBraking) && ((!BuffersOrContinuationNowFlag && !ContinuationNextFlag) ||
4231  // check from the end of the front element, if include the front element and could brake during it, then will skip further loops
4232  // & miss a stop requirement just beyond the front element. happened in Richard Standing's railway where a new service introduced
4233  // on a 100m length, with 20m length after & then a red signal - train accelerated over the 100m then caused a SPAD as too short a
4234  // stopping distance after it.
4235 
4236  //(!BuffersOrContinuationNowFlag && !ContinuationNextFlag) true when no continuation on either the next element and next but one element.
4237  //There won't be a buffer on the next element or would have caught earlier, just using this flag for convenience.
4238 
4239  // If SignallerStoppingFlag then don't exit loop because of an imminent continuation, because continuation
4240  // not immediately in front (if it is then LeadElement will be the continuation & SignallerStoppingFlag will be reset in UpdateTrain()),
4241  // need to at least give a chance to stop on signaller command, if keep moving until continuation is immediately in front then will
4242  // exit loop & that is OK as don't want to stop so close to a continuation, if that happens it means that the command to stop was given
4243  // too late
4244 
4245  // set final braking or acc'n speed & time values
4246  if(BrakeRate > 0.01)
4247  {
4248  ExitSpeedHalfSquared = (EntrySpeed * EntrySpeed) - (3.6 * 3.6 * 2 * BrakeRate * EntryHalfLength);
4249  if(ExitSpeedHalfSquared < 10)
4250  {
4251  ExitSpeedHalf = 0;
4252  }
4253  else
4254  {
4255  ExitSpeedHalf = sqrt(ExitSpeedHalfSquared);
4256  }
4257  ExitTimeHalf = EntryTime + TDateTime((EntrySpeed - ExitSpeedHalf) / 3.6 / BrakeRate / 86400.0);
4258  ExitSpeedFullSquared = (EntrySpeed * EntrySpeed) - (3.6 * 3.6 * 4 * BrakeRate * EntryHalfLength);
4259  if(ExitSpeedFullSquared < 10)
4260  {
4261  ExitSpeedFull = 0;
4262  }
4263  else
4264  {
4265  ExitSpeedFull = sqrt(ExitSpeedFullSquared);
4266  }
4267  ExitTimeFull = EntryTime + TDateTime((EntrySpeed - ExitSpeedFull) / 3.6 / BrakeRate / 86400.0);
4268  }
4269  else
4270  {
4271  // for accelerating the energy input rate (PowerAtRail) is constant so the following formuli are used:-
4272  // (1) V^2/(3.6^2) - U^2/(3.6^2) = A^2T; (2) V = 3.6 * ((1.5*S*A^2) + U^3/ (3.6)^3)^0.333334;
4273  // where A is a constant (2*PowerAtRail/Mass)^0.5; V = final speed in kph, U = initial speed in kph , S = distance in m & T = time
4274  // This is a bit unrealistic during the early acceleration phase as it will be too rapid, but shouldn't affect the running unduly
4275 
4276  BrakeRate = 0;
4277  ExitSpeedHalf = 3.6 * Power(((1.5 * EntryHalfLength * AValue * AValue) + (EntrySpeed * EntrySpeed * EntrySpeed / 3.6 / 3.6 / 3.6)), 0.333334);
4278  ExitSpeedFull = 3.6 * Power(((3 * EntryHalfLength * AValue * AValue) + (EntrySpeed * EntrySpeed * EntrySpeed / 3.6 / 3.6 / 3.6)), 0.333334);
4279  // above valid for ExitSpeedHalf & Full <= MaxExitSpeed
4281  {
4282  if(PowerAtRail > 1)
4283  // added at v2.4.0 in case reach here with failed train, when can't use AValue in denominator as close zero
4284  {
4285  // [km/h/3.6 = m/s]
4286  ExitTimeHalf = EntryTime + TDateTime(((ExitSpeedHalf * ExitSpeedHalf) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue)
4287  / 86400.0);
4288  }
4289  else
4290  {
4291  ExitTimeHalf = EntryTime + TDateTime(EntryHalfLength * 3.6 / EntrySpeed / 86400.0);
4292  }
4294  // (ExitSpeedHalf <= MaxExitSpeed) & (ExitSpeedFull <= MaxExitSpeed)
4295  {
4296  if(PowerAtRail > 1)
4297  // added at v2.4.0 in case reach here with failed train, when can't use AValue in denominator as close zero
4298  {
4299  // [km/h/3.6 = m/s]
4300  ExitTimeFull = EntryTime + TDateTime(((ExitSpeedFull * ExitSpeedFull) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue)
4301  / 86400.0);
4302  }
4303  else
4304  {
4305  ExitTimeFull = EntryTime + TDateTime(2 * EntryHalfLength * 3.6 / EntrySpeed / 86400.0);
4306  }
4307  }
4308  else // (ExitSpeedHalf <= MaxExitSpeed) & (ExitSpeedFull > MaxExitSpeed)
4309  {
4310  // added at v0.6 as a safeguard
4311  if(MaxExitSpeed < EntrySpeed)
4312  {
4314  }
4315  // to prevent DeltaExitTimeToMaxInSecs being negative
4316  if(MaxExitSpeed < 1)
4317  {
4318  MaxExitSpeed = 1; // to prevent divide by zero error
4319  }
4320  // below as was
4322  double DeltaExitTimeToMaxInSecs;
4323  double DistanceToMax;
4324  if(PowerAtRail > 1) // added at v2.4.0
4325  {
4326  DeltaExitTimeToMaxInSecs = ((MaxExitSpeed * MaxExitSpeed) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue);
4327  DistanceToMax = ((MaxExitSpeed * MaxExitSpeed * MaxExitSpeed) - (EntrySpeed * EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / 3.6 /
4328  (1.5 * AValue * AValue);
4329  }
4330  else // shouldn't ever get here given that ExitSpeedFull > ExitSpeedHalf
4331  {
4332  DeltaExitTimeToMaxInSecs = 2 * EntryHalfLength * 3.6 / EntrySpeed;
4333  // these not really accurate but will be good enough
4334  DistanceToMax = EntryHalfLength;
4335  }
4336  double RemainingDistance = double(FrontElementLength) - DistanceToMax;
4337  double DeltaRemainingTimeInSecs = 3.6 * RemainingDistance / MaxExitSpeed;
4338  ExitTimeFull = EntryTime + TDateTime((DeltaExitTimeToMaxInSecs + DeltaRemainingTimeInSecs) / 86400.0);
4339  }
4340  }
4341  else // ExitSpeedHalf > MaxExitSpeed, ExitSpeedFull must be > MaxExitSpeed
4342  {
4343  // added at v0.6 as a safeguard
4344  if(MaxExitSpeed < EntrySpeed)
4345  {
4347  }
4348  // to prevent DeltaExitTimeToMaxInSecs being negative
4349  if(MaxExitSpeed < 1)
4350  {
4351  MaxExitSpeed = 1; // to prevent divide by zero error
4352  }
4353  // below as was
4355  double DeltaExitTimeToMaxInSecs;
4356  double DistanceToMax;
4357  if(PowerAtRail > 1) // added at v2.4.0
4358  {
4359  DeltaExitTimeToMaxInSecs = ((MaxExitSpeed * MaxExitSpeed) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue);
4360  DistanceToMax = ((MaxExitSpeed * MaxExitSpeed * MaxExitSpeed) - (EntrySpeed * EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / 3.6 /
4361  (1.5 * AValue * AValue);
4362  }
4363  else
4364  {
4365  DeltaExitTimeToMaxInSecs = 2 * EntryHalfLength * 3.6 / EntrySpeed;
4366  // these not really accurate but will be good enough
4367  DistanceToMax = EntryHalfLength / 2;
4368  }
4369  double RemainingDistance = double(FrontElementLength / 2) - DistanceToMax; // remaining distance to half length
4370  double DeltaRemainingTimeInSecs = 3.6 * RemainingDistance / MaxExitSpeed;
4371  ExitTimeHalf = EntryTime + TDateTime((DeltaExitTimeToMaxInSecs + DeltaRemainingTimeInSecs) / 86400.0);
4373  ExitTimeFull = ExitTimeHalf + TDateTime(3.6 * EntryHalfLength / MaxExitSpeed / 86400.0);
4374  }
4375  }
4376  }
4377 
4378  else // SPADFlag set
4379  {
4381  ExitSpeedHalfSquared = (EntrySpeed * EntrySpeed) - (3.6 * 3.6 * 2 * BrakeRate * EntryHalfLength);
4382  if(ExitSpeedHalfSquared < 10)
4383  {
4384  ExitSpeedHalf = 0;
4385  }
4386  else
4387  {
4388  ExitSpeedHalf = sqrt(ExitSpeedHalfSquared);
4389  }
4390  ExitTimeHalf = EntryTime + TDateTime((EntrySpeed - ExitSpeedHalf) / 3.6 / BrakeRate / 86400.0);
4391  ExitSpeedFullSquared = (EntrySpeed * EntrySpeed) - (3.6 * 3.6 * 4 * BrakeRate * EntryHalfLength);
4392  if(ExitSpeedFullSquared < 10)
4393  {
4394  ExitSpeedFull = 0;
4395  }
4396  else
4397  {
4398  ExitSpeedFull = sqrt(ExitSpeedFullSquared);
4399  }
4400  ExitTimeFull = EntryTime + TDateTime((EntrySpeed - ExitSpeedFull) / 3.6 / BrakeRate / 86400.0);
4401 
4402  // check if the exit speed is < 80% of the stopping speed for the next element, and if so stop at end of this element
4403  // this is because would stop short of end of next element (in reality the time to reach the end of the next element
4404  // would be too short (could be so short as to make the train jump) as time is calculated purely on speed & brake rate);
4405  // 80% is used as the brake rate might be set to come to a halt at the end of the next element in which case the speed
4406  // will be the stopping speed.
4407  if(ExitSpeedFull > 0)
4408  {
4409  if(Track->TrackElementAt(746, CurrentTrackVectorPosition).TrackType == Points)
4410  {
4411  if((EntryPos == 0) || (EntryPos == 2))
4412  {
4413  if(Track->TrackElementAt(747, CurrentTrackVectorPosition).Attribute == 0)
4414  {
4415  ExitPos = 1;
4416  }
4417  else
4418  {
4419  ExitPos = 3;
4420  }
4421  }
4422  else
4423  {
4424  ExitPos = 0;
4425  }
4426  }
4427  else
4428  {
4429  ExitPos = Track->GetNonPointsOppositeLinkPos(EntryPos);
4430  }
4431  NextTrackVectorPosition = Track->TrackElementAt(748, CurrentTrackVectorPosition).Conn[ExitPos];
4432  NextEntryPos = Track->TrackElementAt(749, CurrentTrackVectorPosition).ConnLinkPos[ExitPos];
4433  if(NextTrackVectorPosition > -1) // not a continuation or buffer
4434  {
4435  int NextElementLength;
4436  if(NextEntryPos > 1)
4437  {
4438  NextElementLength = (Track->TrackElementAt(750, NextTrackVectorPosition).Length23);
4439  }
4440  else
4441  {
4442  NextElementLength = (Track->TrackElementAt(751, NextTrackVectorPosition).Length01);
4443  }
4444  double NextStoppingSpeed = sqrt(3.6 * 3.6 * 2 * BrakeRate * NextElementLength);
4445  if(ExitSpeedFull < (0.8 * NextStoppingSpeed))
4446  {
4447  ExitSpeedFull = 0;
4448  }
4449  }
4450  }
4451  }
4452  // allow all values to be set normally in case need to brake, then test for zero power & need to coast
4453  if(PowerAtRail < 1) // new at v2.4.0 note that km/h/3.6 = m/s
4454  {
4455  // bring to a stop in 20 elements at 100km/h & assume each 100m long for calculating exit times but if on a continuation maintain speed
4456  if(LeadElement > -1)
4457  {
4459  // don't stop on a continuation either entering or leaving
4460  {
4463  MaxExitSpeed = LimitingSpeed;
4464  BrakeRate = 0;
4465  ExitTimeHalf = EntryTime + TDateTime((50 * 3.6 / (EntrySpeed) / 86400.0));
4466  // assume length is 50m for ease of calc
4467  ExitTimeFull = ExitTimeHalf + TDateTime((50 * 3.6 / (ExitSpeedHalf) / 86400.0));
4468  Utilities->CallLogPop(2126);
4469  return;
4470  }
4471  }
4472  else if(MidElement > -1)
4473  {
4475  {
4478  MaxExitSpeed = LimitingSpeed;
4479  BrakeRate = 0;
4480  ExitTimeHalf = EntryTime + TDateTime((50 * 3.6 / (EntrySpeed) / 86400.0));
4481  // assume length is 50m for ease of calc
4482  ExitTimeFull = ExitTimeHalf + TDateTime((50 * 3.6 / (ExitSpeedHalf) / 86400.0));
4483  Utilities->CallLogPop(2127);
4484  return;
4485  }
4486  }
4487  else if(LagElement > -1)
4488  {
4490  {
4493  MaxExitSpeed = LimitingSpeed;
4494  BrakeRate = 0;
4495  ExitTimeHalf = EntryTime + TDateTime((50 * 3.6 / (EntrySpeed) / 86400.0));
4496  // assume length is 50m for ease of calc
4497  ExitTimeFull = ExitTimeHalf + TDateTime((50 * 3.6 / (ExitSpeedHalf) / 86400.0));
4498  Utilities->CallLogPop(2128);
4499  return;
4500  }
4501  }
4502  if(EntrySpeed > 7.5) // keep going for at least another element
4503  {
4504  ExitSpeedHalf = EntrySpeed - 2.5;
4505  ExitSpeedFull = EntrySpeed - 5;
4506  MaxExitSpeed = LimitingSpeed;
4507  BrakeRate = 0;
4508  ExitTimeHalf = EntryTime + TDateTime((50 * 3.6 / (EntrySpeed - 1.25) / 86400.0));
4509  // assume length is 50m for ease of calc
4510  ExitTimeFull = ExitTimeHalf + TDateTime((50 * 3.6 / (ExitSpeedHalf - 1.25) / 86400.0));
4511  Utilities->CallLogPop(2129);
4512  return;
4513  }
4514  else // stop immediately, don't enter next element, floating label had displayed last exit speed full on 2nd half move so with zero when fully on element
4515  {
4516  // will appear to have slowed at steady rate
4517  ExitSpeedHalf = 0;
4518  ExitSpeedFull = 0;
4519  MaxExitSpeed = LimitingSpeed;
4520  BrakeRate = 0;
4521  ExitTimeHalf = EntryTime + TDateTime(1/24); //set this high in case used later though unlikely
4522  ExitTimeFull = EntryTime + TDateTime(1/23); //set about 2.5 mins later than half time
4523  StoppedWithoutPower = true;
4524  Utilities->CallLogPop(2130);
4525  return;
4526  }
4527  }
4528  // TempBrakeRate=MinSingle; TempBrakeRate=MaxSingle; TempBrakeRate=MinDouble; TempBrakeRate=MaxDouble;//included to stop warnings from unused declarations in math.hpp
4529  // TempBrakeRate=MinExtended; TempBrakeRate=MaxExtended; TempBrakeRate=MinComp; TempBrakeRate=MaxComp;//included to stop warnings from unused declarations in math.hpp
4530  Utilities->CallLogPop(707);
4531 }
4532 // ---------------------------------------------------------------------------
4533 /*
4534  bool TTrain::IsTerminalStation(int TrackVectorPosition, int EntryPos)
4535  {
4536  int NextExitPos;
4537  TTrackElement NextElement = Track->TrackElementAt(379, TrackVectorPosition), TempElement;
4538  if(TimetableVector.empty()) return false;
4539  if(NextElement.ActiveTrackElementName != TimetableVector.begin()->LocationName) return false;
4540  while((NextElement.ActiveTrackElementName == TimetableVector.begin()->LocationName) && (NextElement.TrackType != Buffers))
4541  {
4542  //check for points & follow attribute, but don't worry about a derail as that dealt with elsewhere
4543  if((NextElement.TrackType != Points) || ((EntryPos != 0) && (EntryPos != 2)))
4544  {
4545  NextExitPos = Track->GetNonPointsOppositeLinkPos(EntryPos);
4546  }
4547  else if((NextElement.TrackType == Points) && ((EntryPos == 0) || (EntryPos == 2)))
4548  {
4549  if(NextElement.Attribute == 0) NextExitPos = 1; else NextExitPos = 3;
4550  }
4551  TempElement = Track->TrackElementAt(380, NextElement.Conn[NextExitPos]);//need temp as NextElement used in next step
4552  NextElement = TempElement;
4553  }
4554  if(NextElement.ActiveTrackElementName != TimetableVector.begin()->LocationName) return false;
4555  if(NextElement.TrackType == Buffers) return true;
4556  return false;//shouldn't reach here but include to prevent compiler return warning
4557  }
4558 */
4559 // ---------------------------------------------------------------------------
4560 
4561 int TTrain::NameInTimetableBeforeCDT(int Caller, AnsiString Name, bool &Stop)
4562 /*
4563  returns the number by which the train ActionVectorEntryPtr needs
4564  to be incremented to point to the location arrival entry or passtime entry before a change of direction. Used to display missed
4565  actions when a stop or pass location has been reached before other timetabled events have been carried out. If can't find it, or Name
4566  is "", -1 is returned. A change of direction is the limit of the search because a train may not stop at a location on the way out
4567  but stop on way back, and in these circumstances no actions have been missed. Stop indicates whether the train will stop at (true)
4568  or pass (false) the location.
4569 */{
4570  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",NameInTimetableBeforeCDT," + Name + "," + HeadCode);
4571  Stop = false;
4572  if(TimetableFinished || (Name == ""))
4573  {
4574  Utilities->CallLogPop(957);
4575  return(-1);
4576  }
4577  // start looking from current pointer position
4578  for(TActionVectorEntry *Ptr = ActionVectorEntryPtr; Ptr < &TrainDataEntryPtr->ActionVector.back(); Ptr++)
4579  {
4580  if((Ptr->Command == "cdt") || (Ptr->FormatType == Repeat))
4581  {
4582  break;
4583  }
4584  if((Ptr->ArrivalTime > TDateTime(-1)) && (Ptr->LocationName == Name))
4585  {
4586  if((Ptr->FormatType == TimeLoc) || (Ptr->FormatType == TimeTimeLoc))
4587  {
4588  Stop = true;
4589  Utilities->CallLogPop(960);
4590  return (Ptr - ActionVectorEntryPtr);
4591  }
4592  }
4593  if((Ptr->EventTime > TDateTime(-1)) && (Ptr->LocationName == Name) && (Ptr->Command == "pas"))
4594  {
4595  Utilities->CallLogPop(1517);
4596  return (Ptr - ActionVectorEntryPtr);
4597  }
4598  }
4599  Utilities->CallLogPop(959);
4600  return(-1); // not found a valid entry
4601 }
4602 
4603 // ---------------------------------------------------------------------------
4604 
4606 /* Checks forward from train LeadElement, following leading point attributes but ignoring trailing point attributes,
4607  until finds either a train or a signal/buffers/continuation/loop. If finds a train returns false, else returns true.
4608  Ignores the call-on signal.
4609 */{
4610  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ClearToNextSignal" + "," + HeadCode);
4611  int ReturnVal = 0;
4612 
4613  for(unsigned int x = 0; x < Track->TrackVector.size(); x++)
4614  {
4615  Track->TrackElementAt(1031, x).TempTrackMarker01 = false;
4616  Track->TrackElementAt(1032, x).TempTrackMarker23 = false;
4617  }
4618  int CurrentTrackVectorPosition = LeadElement, NextTrackVectorPosition;
4619  int EntryPos = LeadEntryPos, ExitPos, NextEntryPos;
4620 
4621  while(true)
4622  {
4623  if((Track->TrackElementAt(382, CurrentTrackVectorPosition).TrainIDOnElement > -1) && (Track->TrackElementAt(383,
4624  CurrentTrackVectorPosition).TrainIDOnElement != TrainID))
4625  {
4626  ReturnVal = 1;
4627  break;
4628  }
4629  if(((Track->TrackElementAt(384, CurrentTrackVectorPosition).TrackType == Buffers) || (Track->TrackElementAt(385,
4630  CurrentTrackVectorPosition).TrackType == Continuation)) && (EntryPos == 1))
4631  {
4632  ReturnVal = 2;
4633  break;
4634  }
4635  if((EntryPos < 2) && (Track->TrackElementAt(386, CurrentTrackVectorPosition).Config[1 - EntryPos] == Signal) && (Track->TrackElementAt(529,
4636  CurrentTrackVectorPosition).Attribute != 4)) // Attr 4 == call-on signal
4637  {
4638  ReturnVal = 3;
4639  break;
4640  }
4641  if((Track->TrackElementAt(387, CurrentTrackVectorPosition).TrackType == Bridge) || (Track->TrackElementAt(388,
4642  CurrentTrackVectorPosition).TrackType == Crossover))
4643  {
4644  if((EntryPos < 2) && (Track->TrackElementAt(523, CurrentTrackVectorPosition).TempTrackMarker01))
4645  // must be a loop - reached same point as examined earlier
4646  {
4647  ReturnVal = 4;
4648  break;
4649  }
4650  else if((EntryPos > 1) && (Track->TrackElementAt(524, CurrentTrackVectorPosition).TempTrackMarker23))
4651  {
4652  ReturnVal = 4;
4653  break;
4654  }
4655  }
4656  else
4657  {
4658  if((Track->TrackElementAt(525, CurrentTrackVectorPosition).TempTrackMarker01) || (Track->TrackElementAt(526,
4659  CurrentTrackVectorPosition).TempTrackMarker23))
4660  {
4661  ReturnVal = 4;
4662  break;
4663  }
4664  }
4665  if(EntryPos < 2)
4666  {
4667  Track->TrackElementAt(389, CurrentTrackVectorPosition).TempTrackMarker01 = true;
4668  }
4669  else
4670  {
4671  Track->TrackElementAt(527, CurrentTrackVectorPosition).TempTrackMarker23 = true;
4672  }
4673  if(Track->TrackElementAt(390, CurrentTrackVectorPosition).TrackType == Points)
4674  {
4675  if((EntryPos == 0) || (EntryPos == 2))
4676  {
4677  if(Track->TrackElementAt(391, CurrentTrackVectorPosition).Attribute == 0)
4678  {
4679  ExitPos = 1;
4680  }
4681  else
4682  {
4683  ExitPos = 3;
4684  }
4685  }
4686  else
4687  {
4688  ExitPos = 0;
4689  }
4690  }
4691  else
4692  {
4693  ExitPos = Track->GetNonPointsOppositeLinkPos(EntryPos);
4694  }
4695  NextTrackVectorPosition = Track->TrackElementAt(392, CurrentTrackVectorPosition).Conn[ExitPos];
4696  NextEntryPos = Track->TrackElementAt(393, CurrentTrackVectorPosition).ConnLinkPos[ExitPos];
4697  CurrentTrackVectorPosition = NextTrackVectorPosition;
4698  EntryPos = NextEntryPos;
4699  }
4700  if(ReturnVal == 1)
4701  {
4702  Utilities->CallLogPop(708);
4703  return(false);
4704  }
4705  if(ReturnVal == 2)
4706  {
4707  Utilities->CallLogPop(709);
4708  return(true);
4709  }
4710  if(ReturnVal == 3)
4711  {
4712  Utilities->CallLogPop(946);
4713  return(true);
4714  }
4715  if(ReturnVal == 4)
4716  {
4717  Utilities->CallLogPop(947);
4718  return(true);
4719  }
4720  else
4721  {
4722  throw Exception("Error - failed to set ReturnVal in ClearToNextSignal()");
4723  }
4724 }
4725 
4726 // ---------------------------------------------------------------------------
4727 
4729 /*
4730  Check whether calling-on conditions met - a) approaching train has stopped at a signal but not at a location;
4731  b) if there is a facing train at the station, it is being held appropriately (must be awaiting a join (Fjo or jbo) or a
4732  change of direction (cdt), remaining here (Frh), or under signaller control);
4733  c) at least 1 platform available for the approaching train; d) points (if any) set for direct route into platform;
4734  e) approaching train is to stop at station; f) no more facing signals between train and platform; g) [dropped g]
4735  h) train in front preventing route being set far enough to release stop signal; i) train in front not exiting at continuation; j) signal must be within 4km of
4736  the stop platform; k) [dropped (k), now can set a reoute or part route into platform so can set points more easily.] l) no existing route conflicts with the route into the platform,
4737  m) not failed or stopped without power
4738  If all OK & route or part route not already set then set an unrestricted route into the station (just to the first platform), to prevent point changing or other route conflicts - if a partial route set than can still
4739  change points outside the route or have a route conflict if another route is set.
4740 */{
4741  if(Track->RouteFlashFlag || TrainFailed || StoppedWithoutPower) //failed & no power conditions added at v2.10.0
4742  {
4743  return(false); // don't want to create a new route from the stop signal if one is already in construction & can't call on if failed or no power
4744  }
4745  // some of the callingon route elements may be involved
4746  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CallingOnAllowed" + "," + HeadCode);
4747  bool PlatformFoundFlag = false, StopRequired = false, SkipRouteCheck = false, RouteOrPartRouteSet = false; // last added at v1.2.0
4748  int CurrentTrackVectorPosition = LeadElement, NextTrackVectorPosition, ElementNumber = 0, Distance = 0;
4749  int RouteStartPosition;
4750  // this is the track vector position of the start element for the new unrestricted route - one past the stop signal
4751  int PlatformPosition;
4752  // the track vector position of the first stop platfrom
4753  int EntryPos = LeadEntryPos, ExitPos, NextEntryPos, RouteID;
4754  // not used here
4755  AnsiString LeadStationName = Track->TrackElementAt(395, LeadElement).ActiveTrackElementName; // still OK even if ""
4756  int LeadElementDistance = Track->TrackElementAt(1017, LeadElement).Length01; //added after 2.7.0 as don't want to add this to overall distance since train has already covered this distance
4757  // use Length01, may be wrong for points/crossovers/bridges but unlikely to occur in practice
4758  // must be stopped at a signal but not at a location & still in timetable (a)
4760  // no need to check for SignallerStopped as this function only called in Timetable mode
4761  {
4762  Utilities->CallLogPop(711);
4763  return(false);
4764  }
4765  while(true)
4766  {
4767  TTrackElement &CurrentTrackElement = Track->TrackElementAt(396, CurrentTrackVectorPosition);
4768  // don't look further than 4km ahead (j)
4769  if(Distance > (4000 + LeadElementDistance))
4770  {
4771  Utilities->CallLogPop(967);
4772  return(false);
4773  }
4774  // if find another train on an element in front, before find a valid platform, return false (c)
4775  if((CurrentTrackElement.TrainIDOnElement > -1) && (CurrentTrackElement.TrainIDOnElement != TrainID) && !PlatformFoundFlag)
4776  {
4777  Utilities->CallLogPop(713);
4778  return(false);
4779  }
4780  // if find another train in front when there is a valid platform (keep searching after find a platform as train may still
4781  // be facing later on)
4782  if((CurrentTrackElement.TrainIDOnElement > -1) && (CurrentTrackElement.TrainIDOnElement != TrainID) && PlatformFoundFlag)
4783  {
4784  // get LeadElement, if -1 return (could be exiting at continuation) (i)
4785  TTrain OtherTrain = TrainController->TrainVectorAtIdent(12, CurrentTrackElement.TrainIDOnElement);
4786  if(OtherTrain.LeadElement == -1)
4787  {
4788  Utilities->CallLogPop(714);
4789  return(false);
4790  }
4791  // if a facing train then make sure it is awaiting a join (Fjo or jbo) or a change of direction (cdt), or remaining here (Frh) (b)
4792  if(OtherTrain.LeadElement == CurrentTrackVectorPosition)
4793  {
4794  AnsiString OtherCommand = OtherTrain.ActionVectorEntryPtr->Command;
4795  if((OtherCommand == "Fjo") || (OtherCommand == "jbo") || (OtherCommand == "cdt") || (OtherCommand == "Frh") ||
4796  (OtherTrain.TrainMode == Signaller))
4797  {
4798  break;
4799  }
4800  else
4801  {
4802  Utilities->CallLogPop(955);
4803  return(false);
4804  }
4805  }
4806  else // (h)
4807  {
4808  break;
4809  }
4810  }
4811  // if reach buffers or exit continuation return false (can set route)
4812  if(((CurrentTrackElement.TrackType == Buffers) || (CurrentTrackElement.TrackType == Continuation)) && (EntryPos == 1))
4813  {
4814  Utilities->CallLogPop(716);
4815  return(false);
4816  }
4817  // if reach forward signal (other than the one the train is waiting at) return false (can set route) (f)
4818  if((EntryPos < 2) && (CurrentTrackElement.Config[1 - EntryPos] == Signal) && (CurrentTrackVectorPosition != Track->TrackElementAt(404,
4820  {
4821  Utilities->CallLogPop(717);
4822  return(false);
4823  }
4824  // if reach a location that isn't in timetable return false - drop this as still can't set a route
4825 /*
4826  if((Track->TrackElementAt(405, CurrentTrackVectorPosition).ActiveTrackElementName != "") && (Track->TrackElementAt(406, CurrentTrackVectorPosition).ActiveTrackElementName != LeadStationName) &&
4827  (NameInTimetableBeforeCDT(14, Track->TrackElementAt(407, CurrentTrackVectorPosition).ActiveTrackElementName) == -1))
4828  {
4829  Utilities->CallLogPop(718);
4830  return false;
4831  }
4832 */
4833  // if reach a location that is in timetable set PlatformFoundFlag (but not if position is points set to diverge) (e)
4834  if((CurrentTrackElement.ActiveTrackElementName != "") && (CurrentTrackElement.ActiveTrackElementName != LeadStationName) &&
4835  (NameInTimetableBeforeCDT(15, CurrentTrackElement.ActiveTrackElementName, StopRequired) > -1))
4836  {
4837  if(StopRequired)
4838  {
4839  if((CurrentTrackElement.TrackType != Points) || ((CurrentTrackElement.TrackType == Points) && (CurrentTrackElement.Attribute == 0)))
4840  {
4841  if(!PlatformFoundFlag)
4842  {
4843  PlatformPosition = CurrentTrackVectorPosition;
4844  }
4845  // ensure this only set once at first valid platform position - the unrestricted route will end here
4846  PlatformFoundFlag = true;
4847  }
4848  }
4849  }
4850  // Drop this below - was to prevent call-on if front train had left the station. Criterion now is not that front
4851  // train has to be at station but that has to be before the next forward signal
4852 /*
4853  if((Track->TrackElementAt(411, CurrentTrackVectorPosition).ActiveTrackElementName == "") && (PlatformFoundFlag))
4854  {
4855  Utilities->CallLogPop(719);
4856  return false;
4857  }
4858 */
4859  // make sure points are followed correctly (d) & set ExitPos
4860  if(CurrentTrackElement.TrackType == Points)
4861  {
4862  if((EntryPos == 0) || (EntryPos == 2))
4863  {
4864  if(CurrentTrackElement.Attribute == 0)
4865  {
4866  ExitPos = 1;
4867  }
4868  else
4869  {
4870  ExitPos = 3;
4871  }
4872  }
4873  if(EntryPos == 1)
4874  {
4875  if(CurrentTrackElement.Attribute == 0)
4876  {
4877  ExitPos = 0;
4878  }
4879  else
4880  {
4881  Utilities->CallLogPop(720);
4882  return(false);
4883  }
4884  }
4885  if(EntryPos == 3)
4886  {
4887  if(CurrentTrackElement.Attribute == 1)
4888  {
4889  ExitPos = 0;
4890  }
4891  else
4892  {
4893  Utilities->CallLogPop(721);
4894  return(false);
4895  }
4896  }
4897  }
4898  else
4899  {
4900  ExitPos = Track->GetNonPointsOppositeLinkPos(EntryPos);
4901  }
4902  // check existing routes - if element forward of the signal (ElementNumber == 2) is AutoSignals then OK without further checks as this route must extend to
4903  // the next signal so must at least reach the station, also if have another route set (must be unrestricted) from either the stop signal or the element after it
4904  // to or towards the platform (& all points set correctly) then OK, otherwise reject if (1) there are any route elements already set from element
4905  // forward of element after the signal to & including the first platform element (covers crossover with other route set) or (2) a fouled diagonal (k)
4906  if(ElementNumber < 2)
4907  {
4908  SkipRouteCheck = true;
4909  }
4910  else
4911  {
4912  SkipRouteCheck = false;
4913  }
4914  if(ElementNumber == 1) // the stop signal
4915  {
4916  RouteStartPosition = CurrentTrackVectorPosition;
4917  }
4918 /*
4919  if(ElementNumber == 2)
4920  {
4921  if(AllRoutes->GetRouteTypeAndNumber(18, CurrentTrackVectorPosition, EntryPos, RouteID) == AllRoutes->AutoSigsRoute) AutoSigs = true;
4922  else AutoSigs = false;
4923  if(AllRoutes->GetRouteTypeAndNumber(25, CurrentTrackVectorPosition, EntryPos, RouteID) == AllRoutes->NotAutoSigsRoute) OtherFullRouteSet = true;
4924  }
4925 */
4926  if(ElementNumber > 1)
4927  {
4928  if(AllRoutes->GetRouteTypeAndNumber(26, CurrentTrackVectorPosition, EntryPos, RouteID) != AllRoutes->NoRoute)
4929  {
4930  RouteOrPartRouteSet = true;
4931  }
4932  else
4933  {
4934  RouteOrPartRouteSet = false;
4935  }
4936  }
4937  if(!SkipRouteCheck && !RouteOrPartRouteSet)
4938  {
4939  if(AllRoutes->TrackIsInARoute(16, CurrentTrackVectorPosition, EntryPos)) // must be a conflicting route
4940  {
4941  Utilities->CallLogPop(1859);
4942  return(false);
4943  }
4944  int ExitLink = CurrentTrackElement.Link[ExitPos];
4945  if((ExitLink == 1) || (ExitLink == 3) || (ExitLink == 7) || (ExitLink == 9))
4946  {
4947  if(AllRoutes->DiagonalFouledByRouteOrTrain(6, CurrentTrackElement.HLoc, CurrentTrackElement.VLoc, ExitLink))
4948  {
4949  Utilities->CallLogPop(1850);
4950  return(false);
4951  }
4952  }
4953  }
4954  // finished all checks, now update CurrentTrackVectorPosition & EntryPos for the next iteration
4955  if(EntryPos < 2)
4956  {
4957  Distance += CurrentTrackElement.Length01;
4958  }
4959  else
4960  {
4961  Distance += CurrentTrackElement.Length23;
4962  }
4963  NextTrackVectorPosition = CurrentTrackElement.Conn[ExitPos];
4964  NextEntryPos = CurrentTrackElement.ConnLinkPos[ExitPos];
4965  CurrentTrackVectorPosition = NextTrackVectorPosition;
4966  EntryPos = NextEntryPos;
4967  ElementNumber++;
4968  } // while(true)
4969 
4970  // if all OK & autosigs route not already set then set an unrestricted route into the station (just to the first platform)
4971  // from the stop signal (note that it may be last in an autosigs route)
4972  // a single element route at the stop signal should have been removed prior to this function being called (that called before
4973  // this in ClockTimer2)
4974 
4975  // now add elements to the CallonVector
4976  TAllRoutes::TCallonEntry CallonEntry(RouteOrPartRouteSet, RouteStartPosition, PlatformPosition);
4977 
4978  AllRoutes->CallonVector.push_back(CallonEntry);
4979  Utilities->CallLogPop(1860);
4980  return(true); // return false if fail to set route for any reason
4981 }
4982 
4983 // ---------------------------------------------------------------------------
4984 /*
4985  bool TTrain::TimetableFinished()
4986  {
4987  if((ActionVectorEntryPtr == TrainDataEntryPtr->ActionVector.end()) || (ActionVectorEntryPtr->FormatType == Repeat))//past all actions
4988  {
4989  return true;
4990  }
4991  return false;
4992  }
4993 */
4994 // ---------------------------------------------------------------------------
4995 
4996 AnsiString TTrain::GetTrainHeadCode(int Caller)
4997 
4998 {
4999  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetTrainHeadCode" + "," + HeadCode);
5000  AnsiString RepeatHeadCode = TrainController->GetRepeatHeadCode(0, HeadCode, RepeatNumber, IncrementalDigits);
5001 
5002  Utilities->CallLogPop(1452);
5003  return(RepeatHeadCode);
5004 }
5005 
5006 // ---------------------------------------------------------------------------
5007 
5008 TDateTime TTrain::GetTrainTime(int Caller, TDateTime Time)
5009 {
5010  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetTrainTime," + Utilities->Format96HHMMSS(Time));
5011  TDateTime RepeatTime = TrainController->GetRepeatTime(1, Time, RepeatNumber, IncrementalMinutes);
5012 
5013  Utilities->CallLogPop(1453);
5014  return(RepeatTime);
5015 }
5016 
5017 // ---------------------------------------------------------------------------
5018 
5019 bool TTrain::IsThereAnAdjacentTrain(int Caller, TTrain *&TrainToBeJoinedBy)
5020 {
5021  // Used to check for a stopped adjacent train for use in PopUp menu //new at v2.4.0
5022  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",IsThereAnAdjacentTrain" + "," + HeadCode);
5023  // check if there's a stopped adjacent train, if there is but not under sig control give a message in calling function
5024  // first check that train is fully on the railway
5025  bool FrontValid = false, RearValid = false;
5026  TTrackElement FrontAdjacentTrackElement, RearAdjacentTrackElement;
5027 
5028  if((LeadElement == -1) || (MidElement == -1))
5029  {
5030  TrainToBeJoinedBy = NULL;
5031  Utilities->CallLogPop(2131);
5032  return(false);
5033  }
5035  {
5036  FrontAdjacentTrackElement = Track->TrackElementAt(965, (Track->TrackElementAt(966, LeadElement).Conn[LeadExitPos]));
5037  FrontValid = true;
5038  }
5040  {
5041  RearAdjacentTrackElement = Track->TrackElementAt(968, (Track->TrackElementAt(969, MidElement).Conn[MidEntryPos]));
5042  RearValid = true;
5043  }
5044  int TrainToBeJoinedByID = -1;
5045 
5046  // first check if on a 2-track element & select correct ID number if so
5047  if(FrontValid)
5048  {
5049  if(FrontAdjacentTrackElement.TrackType == Bridge)
5050  {
5052  {
5053  TrainToBeJoinedByID = FrontAdjacentTrackElement.TrainIDOnBridgeTrackPos23;
5054  }
5055  else
5056  {
5057  TrainToBeJoinedByID = FrontAdjacentTrackElement.TrainIDOnBridgeTrackPos01;
5058  }
5059  }
5060  else
5061  {
5062  TrainToBeJoinedByID = FrontAdjacentTrackElement.TrainIDOnElement;
5063  }
5064  }
5065  if((TrainToBeJoinedByID < 0) && RearValid)
5066  {
5067  // first check if on a 2-track element & select correct ID number if so
5068  if(RearAdjacentTrackElement.TrackType == Bridge)
5069  {
5071  {
5072  TrainToBeJoinedByID = RearAdjacentTrackElement.TrainIDOnBridgeTrackPos23;
5073  }
5074  else
5075  {
5076  TrainToBeJoinedByID = RearAdjacentTrackElement.TrainIDOnBridgeTrackPos01;
5077  }
5078  }
5079  else
5080  {
5081  TrainToBeJoinedByID = RearAdjacentTrackElement.TrainIDOnElement;
5082  }
5083  }
5084  if(TrainToBeJoinedByID < 0) // no adjacent train
5085  {
5086  TrainToBeJoinedBy = NULL;
5087  Utilities->CallLogPop(2132);
5088  return(false);
5089  }
5090  TrainToBeJoinedBy = &(TrainController->TrainVectorAtIdent(44, TrainToBeJoinedByID));
5091  if(!TrainToBeJoinedBy->Stopped())
5092  {
5093  TrainToBeJoinedBy = NULL;
5094  Utilities->CallLogPop(2133);
5095  return(false);
5096  }
5097  Utilities->CallLogPop(2134);
5098  return(true);
5099 }
5100 
5101 // ---------------------------------------------------------------------------
5102 
5103 void TTrain::LogAction(int Caller, AnsiString OwnHeadCode, AnsiString OtherHeadCode, TActionType ActionType, AnsiString LocationName,
5104  TDateTime TimetableNonRepeatTime, bool Warning)
5105 /*
5106  Time = timetable time, the time adjustments for repeat trains is carried out internally
5107  Not all messages need this, if not needed a dummy value is required but not used
5108 
5109  Arrive: 06:05:40: 2F46 arrived at Old Street 1 minute late
5110  Pass: 06:05:40: 2F46 passed Old Street 1 minute late
5111  Terminate: 06:05:40: 2F46 terminated at Old Street 1 minute late
5112  //NB for Frh just give terminated message but without event time - don't use this function
5113  Depart: 06:05:15: 3F43 departed from Essex Road 2 minutes late
5114  Create: 06:05:40: 2F46 created at Old Street 1 minute late
5115  Enter: 06:05:40: 2F46 entered railway at Old Street 1 minute late
5116  Leave: 06:05:40: 2F46 left railway at 57-N4 1 minute late
5117  FrontSplit: 06:05:40: 2F46 split from front to 3D54 at Old Street 1 minute late
5118  RearSplit: 06:05:40: 2F46 split from rear to 3D54 at Old Street 1 minute late
5119  JoinedByOther: 06:05:40: 2F46 joined by 3D54 at Old Street 1 minute late
5120  ChangeDirection: 06:05:40: 2F46 changed direction at Old Street 1 minute late
5121  NewService: 06:05:40: 2F46 became new service 3D54 at Old Street 1 minute late
5122  TakeManualControl: 06:05:40: 2F46 taken under signaller control at Old Street
5123  RestoreTimetableControl: 06:05:40: 2F46 restored to timetable control at Old Street
5124  RemoveTrain: 06:05:40: 2F46 REMOVED FROM RAILWAY DUE TO CRASH at Old Street
5125  RemoveTrain: 06:05:40: 2F46 REMOVED FROM RAILWAY DUE TO DERAILMENT at Old Street
5126  RemoveTrain: 06:05:40: 2F46 REMOVED FROM RAILWAY at Old Street
5127  SignallerMoveForwards 06:05:40: 2F46 received signaller authority to proceed
5128  SignallerChangeDirection 06:05:40: 2F46 changed direction under signaller control at Old Street
5129  SignallerPassRedSignal 06:05:40: 2F46 received signaller authority to pass red signal
5130  SignallerJoin 06:05:40: 2F46 joined under signaller control by 3D54 at Old Street //new at v2.4.0
5131  TrainFailure 06:05:40: 2F46 suffered an onboard power failure at Old Street //new at v2.4.0
5132  RepairFailedTrain 06:05:40: 2F46 failure repaired at Old Street //new at v2.4.0
5133  SignallerControlStop 06:05:40: 2F46 received signaller instruction to stop
5134  SignallerStop 06:05:40: 2F46 stopped on signaller command
5135  SignallerLeave: 06:05:40: 2F46 left railway under signaller control at 57-N4
5136  SignallerStepForward: 06:05:40: 2F46 received signaller authority to step forward
5137 */{
5138  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",LogAction," + OwnHeadCode + "," + OtherHeadCode + "," +
5139  AnsiString(ActionType) + "," + LocationName + "," + HeadCode);
5140  AnsiString BaseLog = "", WarningBaseLog = "", PerfLog = "", ActionLog = "";
5141  int IntMinsLate = 0;
5142 
5143  // need to set it in case MinsLate == 0, since it isn't tested for that
5144  if(ActionType == Arrive)
5145  {
5146  ActionLog = " arrived at ";
5147  }
5148  if(ActionType == Terminate)
5149  {
5150  if(TerminatedMessageSent) // to avoid it being sent twice
5151  {
5152  Utilities->CallLogPop(1104);
5153  return;
5154  }
5155  ActionLog = " terminated at ";
5156  TerminatedMessageSent = true;
5157  }
5158  if(ActionType == Depart)
5159  {
5160  ActionLog = " departed from ";
5161  }
5162  if(ActionType == Pass)
5163  {
5164  ActionLog = " passed ";
5165  }
5166  if(ActionType == Create)
5167  {
5168  ActionLog = " created at ";
5169  }
5170  if(ActionType == Enter)
5171  {
5172  ActionLog = " entered railway at ";
5173  }
5174  if(ActionType == Leave)
5175  {
5176  ActionLog = " left railway at ";
5177  }
5178  if(ActionType == FrontSplit)
5179  {
5180  ActionLog = " split from front to ";
5181  }
5182  if(ActionType == RearSplit)
5183  {
5184  ActionLog = " split from rear to ";
5185  }
5186  if(ActionType == JoinedByOther)
5187  {
5188  ActionLog = " joined by ";
5189  }
5190  if(ActionType == ChangeDirection)
5191  {
5192  ActionLog = " changed direction at ";
5193  }
5194  if(ActionType == NewService)
5195  {
5196  ActionLog = " became new service ";
5197  }
5198  if(ActionType == TakeSignallerControl)
5199  {
5200  ActionLog = " taken under signaller control at ";
5201  }
5202  if(ActionType == RestoreTimetableControl)
5203  {
5204  ActionLog = " restored to timetable control at ";
5205  }
5206  if(ActionType == RemoveTrain)
5207  {
5208  if(Crashed)
5209  {
5210  ActionLog = " REMOVED FROM RAILWAY DUE TO CRASH at ";
5211  }
5212  else if(Derailed)
5213  {
5214  ActionLog = " REMOVED FROM RAILWAY DUE TO DERAILMENT at ";
5215  }
5216  else
5217  {
5218  ActionLog = " REMOVED FROM RAILWAY at ";
5219  }
5220  }
5221  if(ActionType == SignallerMoveForwards)
5222  {
5223  ActionLog = " received signaller authority to proceed";
5224  }
5225  if(ActionType == SignallerStepForward)
5226  {
5227  ActionLog = " received signaller authority to step forward";
5228  }
5229  if(ActionType == SignallerChangeDirection)
5230  {
5231  ActionLog = " changed direction under signaller control at ";
5232  }
5233  if(ActionType == SignallerPassRedSignal)
5234  {
5235  ActionLog = " received signaller authority to pass red signal";
5236  }
5237  if(ActionType == SignallerControlStop)
5238  {
5239  ActionLog = " received signaller instruction to stop";
5240  }
5241  if(ActionType == SignallerStop)
5242  {
5243  ActionLog = " stopped on signaller instruction ";
5244  }
5245  if(ActionType == SignallerJoin)
5246  {
5247  ActionLog = " joined under signaller control by ";
5248  }
5249  if(ActionType == TrainFailure)
5250  {
5251  ActionLog = " suffered an onboard power failure at ";
5252  }
5253  if(ActionType == RepairFailedTrain)
5254  {
5255  ActionLog = " failure repaired at ";
5256  }
5257  if(ActionType == SignallerLeave)
5258  {
5259  ActionLog = " left railway under signaller control at ";
5260  }
5261  if(OtherHeadCode != "")
5262  {
5263  OtherHeadCode += " at ";
5264  }
5265  TDateTime ActualTime = TrainController->TTClockTime;
5266 
5267  if(Warning)
5268  {
5269  BaseLog = Utilities->Format96HHMMSS(ActualTime) + " WARNING: " + HeadCode + ActionLog + OtherHeadCode + LocationName;
5270  WarningBaseLog = HeadCode + ActionLog + OtherHeadCode + LocationName;
5271  }
5272  else
5273  {
5274  BaseLog = Utilities->Format96HHMMSS(ActualTime) + ": " + HeadCode + ActionLog + OtherHeadCode + LocationName;
5275  }
5276  bool TimePerformance = true;
5277 
5278  if((ActionType == TakeSignallerControl) || (ActionType == RestoreTimetableControl) || (ActionType == RemoveTrain) || (ActionType == SignallerMoveForwards)
5279  || (ActionType == SignallerChangeDirection) || (ActionType == SignallerPassRedSignal) || (ActionType == SignallerControlStop) ||
5280  (ActionType == SignallerStop) || (ActionType == SignallerLeave) || (ActionType == SignallerStepForward) || (ActionType == SignallerJoin) ||
5281  (ActionType == TrainFailure) || (ActionType == RepairFailedTrain))
5282  // SignallerJoin & RepairFailedTrain new at v2.4.0
5283  {
5284  TimePerformance = false;
5285  }
5286  if(TimePerformance)
5287  {
5288  double MinsLate = ((double)(ActualTime - GetTrainTime(1, TimetableNonRepeatTime))) * 1440;
5289  MinsDelayed = float(MinsLate);
5290  if(ActionType == Pass) //added at v2.9.2 to prevent time to act increasing suddenly for early pass times then becoming 'NOW' when stops at signal
5291  {
5292  MinsDelayed = 0;
5293  }
5294  // new v2.2.0 for OpActionPanel, can be positive or negative
5295  if(ActionType == Arrive)
5296  {
5298  }
5299  // since train has just arrived this value is the
5300  // recoverable time available at this stop, so reduce MinsDelayed by this amount to prevent it being
5301  // subtracted from later stop recoverable times.
5302  if(MinsLate < 0)
5303  {
5304  IntMinsLate = int(ceil(MinsLate));
5305  }
5306  if(MinsLate > 0)
5307  {
5308  IntMinsLate = int(floor(MinsLate));
5309  }
5310  if(IntMinsLate == 0)
5311  {
5312  PerfLog = " on time";
5313  }
5314  else if(IntMinsLate == 1)
5315  {
5316  PerfLog = " 1 minute late";
5317  }
5318  else if(IntMinsLate == -1)
5319  {
5320  PerfLog = " 1 minute early";
5321  }
5322  else if(IntMinsLate > 1)
5323  {
5324  PerfLog = " " + AnsiString(IntMinsLate) + " minutes late";
5325  }
5326  else if(IntMinsLate < -1)
5327  {
5328  int PosIntMinsLate = -IntMinsLate;
5329  PerfLog = " " + AnsiString(PosIntMinsLate) + " minutes early";
5330  }
5331  if(LocationName.Pos('-') > 0)
5332  {
5333  PerfLog = "," + PerfLog;
5334  // if a position add a comma to separate vertical position number from number of minutes (better appearance)
5335  }
5336  Display->PerformanceLog(0, BaseLog + PerfLog);
5337  }
5338  else
5339  {
5340  Display->PerformanceLog(1, BaseLog);
5341  }
5342  if(Warning)
5343  {
5344  Display->WarningLog(0, WarningBaseLog);
5345  }
5346  // update statistics
5347  if((ActionType == Arrive) && (IntMinsLate == 0))
5348  {
5350  }
5351  else if((ActionType == Arrive) && (IntMinsLate > 0))
5352  {
5354  TrainController->TotLateArrMins += IntMinsLate;
5355  }
5356  else if((ActionType == Arrive) && (IntMinsLate < 0))
5357  {
5359  TrainController->TotEarlyArrMins += abs(IntMinsLate);
5360  }
5361 
5362  else if((ActionType == Pass) && (IntMinsLate == 0))
5363  {
5365  }
5366  else if((ActionType == Pass) && (IntMinsLate > 0))
5367  {
5369  TrainController->TotLatePassMins += IntMinsLate;
5370  }
5371  else if((ActionType == Pass) && (IntMinsLate < 0))
5372  {
5374  TrainController->TotEarlyPassMins += abs(IntMinsLate);
5375  }
5376 
5377  else if((ActionType == Leave) && (IntMinsLate == 0)) //new at v2.9.1 as had been omitted in error earlier
5378  {
5380  }
5381  else if((ActionType == Leave) && (IntMinsLate > 0))
5382  {
5384  TrainController->TotLateExitMins += IntMinsLate;
5385  }
5386  else if((ActionType == Leave) && (IntMinsLate < 0))
5387  {
5389  TrainController->TotEarlyExitMins += abs(IntMinsLate);
5390  }
5391 
5392  else if((ActionType == Depart) && (IntMinsLate == 0)) //can't depart early
5393  {
5395  }
5396  else if((ActionType == Depart) && (IntMinsLate > 0))
5397  {
5399  TrainController->TotLateDepMins += IntMinsLate;
5400  }
5401  Utilities->CallLogPop(968);
5402 }
5403 
5404 // ---------------------------------------------------------------------------
5405 
5406 void TTrain::TrainHasFailed(int Caller)
5407 {
5408  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TrainHasFailed," + HeadCode);
5409  if(Crashed || Derailed || DerailPending)
5410  {
5411  TrainFailurePending = false;
5412  Utilities->CallLogPop(2135);
5413  return;
5414  }
5415  AnsiString LocName = "";
5416 
5417  if(LeadElement > -1)
5418  {
5420  }
5421  if((LocName == "") && (MidElement > -1))
5422  {
5424  }
5425  if((LocName == "") && LeadElement > -1)
5426  {
5427  LocName = Track->TrackElementAt(974, LeadElement).ElementID;
5428  }
5429  if((LocName == "") && (MidElement > -1))
5430  {
5431  LocName = Track->TrackElementAt(975, MidElement).ElementID;
5432  }
5433  TrainController->StopTTClockMessage(81, HeadCode + " has suffered an onboard power failure at " + LocName);
5434  TrainFailed = true;
5435  TrainFailurePending = false;
5436  CallingOnFlag = false; //added at v2.10.0
5438  PowerAtRail = 0.08;
5439  AValue = sqrt(2 * PowerAtRail / Mass);
5441  // TrainFailed only called when PlotElements properly set to Lead, Mid & Lag elements
5442  if(Stopped())
5443  {
5444  EntrySpeed = 0;
5445  ExitSpeedHalf = 0;
5446  ExitSpeedFull = 0;
5447  MaxExitSpeed = 0;
5448  BrakeRate = 0;
5449  StoppedWithoutPower = true;
5450  }
5452  LogAction(33, HeadCode, "", TrainFailure, LocName, TDateTime(0), true);
5453  // true for warning, TDateTime not used
5454  Utilities->CallLogPop(2136);
5455 }
5456 
5457 // ---------------------------------------------------------------------------
5458 
5459 void TTrain::FrontTrainSplit(int Caller)
5460 {
5461 /*
5462  Split logic is:- at least one of 4 final train positions must overlap with one of original train positions, & final 4 positions
5463  will maximise the number at the location. Note that this function isn't sophisticated enough to account for trains already at the
5464  location in determining the 4 positions, and will give a failure message if a train obstructs any of the 4 positions. In these
5465  circumstances the other train will need to be moved sufficiently away to release all 4 positions, then the train will split.
5466 */
5467  TrainController->LogEvent("" + AnsiString(Caller) + ",FrontTrainSplit" + "," + HeadCode);
5468  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",FrontTrainSplit" + "," + HeadCode);
5469  if(PowerAtRail < 1)
5470  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can split when power restored
5471  {
5473  {
5474  TrainController->StopTTClockMessage(82, HeadCode + ": A train without power can't split");
5475  }
5477  Utilities->CallLogPop(2137);
5478  return;
5479  }
5480  AnsiString LocationName = Track->TrackElementAt(555, LeadElement).ActiveTrackElementName;
5481 
5482  if(LocationName == "")
5483  {
5484  LocationName = Track->TrackElementAt(837, MidElement).ActiveTrackElementName;
5485  }
5486  int FirstNamedElementPos, SecondNamedElementPos, FirstNamedLinkedElementPos, SecondNamedLinkedElementPos;
5487  int RearTrainRearPosition, RearTrainFrontPosition, RearTrainExitPos;
5488  int FrontTrainRearPosition, FrontTrainFrontPosition, FrontTrainExitPos;
5490 
5491  // determine all positions & exits
5492  if(LocationName != "")
5493  {
5494  // if message given only call at ~5 sec intervals
5496  {
5497  FirstNamedElementPos = LeadElement;
5498  if(!Track->ThisNamedLocationLongEnoughForSplit(0, LocationName, FirstNamedElementPos,
5499  // check if possible with LeadElement as First
5500  SecondNamedElementPos, FirstNamedLinkedElementPos, SecondNamedLinkedElementPos))
5501  {
5502  FirstNamedElementPos = MidElement;
5503  if(!Track->ThisNamedLocationLongEnoughForSplit(1, LocationName, FirstNamedElementPos,
5504  // if not then accept second if possible (though if Lead no good hard to see how Mid could work, but leave in)
5505  SecondNamedElementPos, FirstNamedLinkedElementPos, SecondNamedLinkedElementPos))
5506  {
5508  {
5509  TrainController->LogActionError(6, HeadCode, "", FailLocTooShort, LocationName);
5511  }
5512  Utilities->CallLogPop(1009);
5513  return;
5514  }
5515  }
5516  else
5517  {
5518  // if first is possible then check if all 4 positions at location, and if not try the second
5519  int LeadPosA = FirstNamedElementPos;
5520  int LeadPosB = FirstNamedLinkedElementPos;
5521  int LeadPosC = SecondNamedElementPos;
5522  int LeadPosD = SecondNamedLinkedElementPos;
5523  // count number of positions that are at the location
5524  int LeadNumAtLoc = 0;
5525  if(Track->TrackElementAt(758, LeadPosA).ActiveTrackElementName == LocationName)
5526  {
5527  LeadNumAtLoc++;
5528  }
5529  if(Track->TrackElementAt(759, LeadPosB).ActiveTrackElementName == LocationName)
5530  {
5531  LeadNumAtLoc++;
5532  }
5533  if(Track->TrackElementAt(760, LeadPosC).ActiveTrackElementName == LocationName)
5534  {
5535  LeadNumAtLoc++;
5536  }
5537  if(Track->TrackElementAt(761, LeadPosD).ActiveTrackElementName == LocationName)
5538  {
5539  LeadNumAtLoc++;
5540  }
5541  if(LeadNumAtLoc < 4)
5542  {
5543  FirstNamedElementPos = MidElement;
5544  if(!Track->ThisNamedLocationLongEnoughForSplit(4, LocationName, FirstNamedElementPos, SecondNamedElementPos, FirstNamedLinkedElementPos,
5545  SecondNamedLinkedElementPos)) // restore originals
5546  {
5547  FirstNamedElementPos = LeadPosA;
5548  FirstNamedLinkedElementPos = LeadPosB;
5549  SecondNamedElementPos = LeadPosC;
5550  SecondNamedLinkedElementPos = LeadPosD;
5551  }
5552  else // count number at location
5553  {
5554  int MidNumAtLoc = 0;
5555  if(Track->TrackElementAt(762, FirstNamedElementPos).ActiveTrackElementName == LocationName)
5556  {
5557  MidNumAtLoc++;
5558  }
5559  if(Track->TrackElementAt(763, FirstNamedLinkedElementPos).ActiveTrackElementName == LocationName)
5560  {
5561  MidNumAtLoc++;
5562  }
5563  if(Track->TrackElementAt(764, FirstNamedLinkedElementPos).ActiveTrackElementName == LocationName)
5564  {
5565  MidNumAtLoc++;
5566  }
5567  if(Track->TrackElementAt(765, SecondNamedLinkedElementPos).ActiveTrackElementName == LocationName)
5568  {
5569  MidNumAtLoc++;
5570  }
5571  if(LeadNumAtLoc > MidNumAtLoc)
5572  // change back, else keep new values
5573  {
5574  FirstNamedElementPos = LeadPosA;
5575  FirstNamedLinkedElementPos = LeadPosB;
5576  SecondNamedElementPos = LeadPosC;
5577  SecondNamedLinkedElementPos = LeadPosD;
5578  }
5579  }
5580  }
5581  }
5582  }
5583  else
5584  {
5585  Utilities->CallLogPop(1791);
5586  return;
5587  }
5588  }
5589  else
5590  {
5591  throw Exception("Error - LocationName not set in FrontTrainSplit");
5592  }
5593  // set front & rear train parameters
5594  // need RearTrainRearPosition, RearTrainFrontPosition, RearTrainExitPos, FrontTrainRearPosition, FrontTrainFrontPosition & FrontTrainExitPos;
5595  // have LeadElement & MidElement of train defining its direction, & one or other on FirstNamedElementPos
5596  // have FirstNamedElementPos, SecondNamedElementPos, FirstNamedLinkedElementPos & SecondNamedLinkedElementPos from ThisNamedLocationLongEnoughForSplit.
5597  if(LeadElement == FirstNamedElementPos)
5598  {
5599  if(MidElement == SecondNamedElementPos)
5600  {
5601  FrontTrainFrontPosition = FirstNamedLinkedElementPos;
5602  FrontTrainRearPosition = FirstNamedElementPos;
5603  RearTrainFrontPosition = SecondNamedElementPos;
5604  RearTrainRearPosition = SecondNamedLinkedElementPos;
5605  }
5606  else // MidElement must == FirstNamedLinkedElementPos
5607  {
5608  FrontTrainFrontPosition = SecondNamedLinkedElementPos;
5609  FrontTrainRearPosition = SecondNamedElementPos;
5610  RearTrainFrontPosition = FirstNamedElementPos;
5611  RearTrainRearPosition = FirstNamedLinkedElementPos;
5612  }
5613  }
5614  else // MidElement == FirstNamedElementPos
5615  {
5616  if(LeadElement == SecondNamedElementPos)
5617  {
5618  FrontTrainFrontPosition = SecondNamedLinkedElementPos;
5619  FrontTrainRearPosition = SecondNamedElementPos;
5620  RearTrainFrontPosition = FirstNamedElementPos;
5621  RearTrainRearPosition = FirstNamedLinkedElementPos;
5622  }
5623  else // LeadElement must == FirstNamedLinkedElementPos
5624  {
5625  FrontTrainFrontPosition = FirstNamedLinkedElementPos;
5626  FrontTrainRearPosition = FirstNamedElementPos;
5627  RearTrainFrontPosition = SecondNamedElementPos;
5628  RearTrainRearPosition = SecondNamedLinkedElementPos;
5629  }
5630  }
5631  RearTrainExitPos = -1;
5632  for(int x = 0; x < 4; x++)
5633  {
5634  if(Track->TrackElementAt(584, RearTrainRearPosition).Conn[x] == RearTrainFrontPosition)
5635  {
5636  RearTrainExitPos = x;
5637  break;
5638  }
5639  }
5640  if(RearTrainExitPos == -1)
5641  {
5642  throw Exception("Error - RearTrainRearPosition not linked to RearTrainFrontPosition in FrontTrainSplit");
5643  }
5644  FrontTrainExitPos = -1;
5645  for(int x = 0; x < 4; x++)
5646  {
5647  if(Track->TrackElementAt(585, FrontTrainRearPosition).Conn[x] == FrontTrainFrontPosition)
5648  {
5649  FrontTrainExitPos = x;
5650  break;
5651  }
5652  }
5653  if(FrontTrainExitPos == -1)
5654  {
5655  throw Exception("Error - FrontTrainRearPosition not linked to FrontTrainFrontPosition in FrontTrainSplit");
5656  }
5657  // check no train (apart from self) on any of the 4 elements & fail if so
5658  int TrainIDOnRearOfRearTrain, TrainIDOnFrontOfRearTrain, TrainIDOnRearOfFrontTrain, TrainIDOnFrontOfFrontTrain;
5659  TTrackElement RearMostElement = Track->TrackElementAt(574, RearTrainRearPosition);
5660 
5661  if((RearMostElement.TrackType == Bridge) && (RearTrainExitPos > 1))
5662  {
5663  TrainIDOnRearOfRearTrain = RearMostElement.TrainIDOnBridgeTrackPos23;
5664  }
5665  else if((RearMostElement.TrackType == Bridge) && (RearTrainExitPos < 2))
5666  {
5667  TrainIDOnRearOfRearTrain = RearMostElement.TrainIDOnBridgeTrackPos01;
5668  }
5669  else
5670  {
5671  TrainIDOnRearOfRearTrain = RearMostElement.TrainIDOnElement;
5672  }
5673  // RearTrainFrontPosition = RearMostElement.Conn[RearTrainExitPos];
5674  TrainIDOnFrontOfRearTrain = Track->TrackElementAt(575, RearTrainFrontPosition).TrainIDOnElement;
5675  // can't be a bridge
5676  TrainIDOnRearOfFrontTrain = Track->TrackElementAt(576, FrontTrainRearPosition).TrainIDOnElement;
5677  // can't be a bridge
5678  // FrontTrainFrontPosition = Track->TrackElementAt(578,FrontTrainRearPosition).Conn[FrontTrainExitPos];
5679  TTrackElement FrontMostElement = Track->TrackElementAt(577, FrontTrainFrontPosition);
5680 
5681  if((FrontMostElement.TrackType == Bridge) && (FrontTrainExitPos > 1))
5682  {
5683  TrainIDOnFrontOfFrontTrain = FrontMostElement.TrainIDOnBridgeTrackPos23;
5684  }
5685  else if((FrontMostElement.TrackType == Bridge) && (FrontTrainExitPos < 2))
5686  {
5687  TrainIDOnFrontOfFrontTrain = FrontMostElement.TrainIDOnBridgeTrackPos01;
5688  }
5689  else
5690  {
5691  TrainIDOnFrontOfFrontTrain = FrontMostElement.TrainIDOnElement;
5692  }
5693  if(((TrainIDOnRearOfRearTrain > -1) && (TrainIDOnRearOfRearTrain != TrainID)) || ((TrainIDOnFrontOfRearTrain > -1) && (TrainIDOnFrontOfRearTrain != TrainID)
5694  ) || ((TrainIDOnRearOfFrontTrain > -1) && (TrainIDOnRearOfFrontTrain != TrainID)) ||
5695  ((TrainIDOnFrontOfFrontTrain > -1) && (TrainIDOnFrontOfFrontTrain != TrainID)))
5696  {
5698  {
5701  }
5702  // don't advance ActionVectorEntryPtr as need to keep trying, other train may move off eventually
5703  Utilities->CallLogPop(1010);
5704  return;
5705  }
5707  {
5709  }
5710  // reposition existing rear train, need to do this first for 2 reasons - 1) will likely be in the way of the new front train, and 2)
5711  // the new train will likely cause a reallocation of the TrainVector, and if so the reference to the existing train will be invalidated.
5712  // Hence deal with existing train while it references a valid entry in the vector, but retain the Old ActionVectorEntryPtr in a separate
5713  // variable as it is needed for setting up the new train
5714  TActionVectorEntry *OldActionVectorEntryPtr = ActionVectorEntryPtr;
5715 
5716  UnplotTrain(0);
5717  StartSpeed = 0;
5718  RearStartElement = RearTrainRearPosition;
5719  RearStartExitPos = RearTrainExitPos;
5720  StoppedAtLocation = true;
5721  if((PowerAtRail < 1) && EntrySpeed < 1) // added at v2.4.0
5722  {
5723  StoppedWithoutPower = true;
5724  }
5725  PlotStartPosition(3);
5730 
5731  Mass = Mass / 2;
5732  MaxBrakeRate = MaxBrakeRate / 2;
5733  PowerAtRail = PowerAtRail / 2;
5734  AValue = sqrt(2 * PowerAtRail / Mass);
5735  // shouldn't change but include in case not set earlier
5736 
5737  // create new front train
5738 /*
5739  TrainController::AddTrain(int RearPosition, int FrontPosition, AnsiString HeadCode, int StartSpeed, int Mass,
5740  double MaxRunningSpeed, double MaxBrakeRate, double PowerAtRail, AnsiString ModeStr, TTrainDataEntry *TrainDataEntryPtr,
5741  int RepeatNumber, int IncrementalMinutes, int SignallerSpeed)
5742 */
5743  // same Mass, MaxBrakeRate & PowerAtRail as this train's halved values, and same MaxRunningSpeed as this train
5744  TActionEventType EventType = NoEvent;
5745 
5746  if(!TrainController->AddTrain(0, FrontTrainRearPosition, FrontTrainFrontPosition, OtherHeadCode, 0, Mass, MaxRunningSpeed, MaxBrakeRate, PowerAtRail,
5747  "Timetable", OldActionVectorEntryPtr->LinkedTrainEntryPtr, RepeatNumber, IncrementalMinutes, IncrementalDigits, SignallerMaxSpeed, false, EventType))
5748  // false for SignallerControl
5749  {
5750  Utilities->CallLogPop(1721); // EventType not used here
5751  // if fails either a throw will have been sent in AddTrain or start position failed prob because of
5752  // another train, in which case a message will have been sent to the perf log, also might well clear later
5753  // when other train moves away
5754  return;
5755  }
5756  // Note data in 'this' now probably invalid as there has been a new addition to the TrainVector, so the train is likely to have a new address, hence make no more changes for the current train
5757  // see mods in UpdateTrain for v1.3.2
5758  TrainController->TrainAdded = true;
5759 
5760  TTrainOperatingData &TTOD = OldActionVectorEntryPtr->LinkedTrainEntryPtr->TrainOperatingDataVector.at(RepeatNumber); // this is for the newly created train
5761 
5762  TTOD.TrainID = TrainController->TrainVector.back().TrainID;
5763  TTOD.RunningEntry = Running;
5764  Utilities->CallLogPop(998);
5765 }
5766 
5767 // ---------------------------------------------------------------------------
5768 
5769 void TTrain::RearTrainSplit(int Caller)
5770 {
5771 /*
5772  Split logic is:- at least one of 4 final train positions must overlap with one of original train positions, & final 4 positions
5773  will maximise the number at the location. Note that this function isn't sophisticated enough to account for trains already at the
5774  location in determining the 4 positions, and will give a failure message if a train obstructs any of the 4 positions. In these
5775  circumstances the other train will need to be moved sufficiently away to release all 4 positions, then the train will split.
5776 */
5777  TrainController->LogEvent("" + AnsiString(Caller) + ",RearTrainSplit" + "," + HeadCode);
5778  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",RearTrainSplit" + "," + HeadCode);
5779  if(PowerAtRail < 1)
5780  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can split when power restored
5781  {
5783  {
5784  TrainController->StopTTClockMessage(83, HeadCode + ": A train without power can't split");
5785  }
5787  Utilities->CallLogPop(2138);
5788  return;
5789  }
5790  AnsiString LocationName = Track->TrackElementAt(587, LeadElement).ActiveTrackElementName;
5791 
5792  if(LocationName == "")
5793  {
5794  LocationName = Track->TrackElementAt(838, MidElement).ActiveTrackElementName;
5795  }
5796  int FirstNamedElementPos, SecondNamedElementPos, FirstNamedLinkedElementPos, SecondNamedLinkedElementPos;
5797  int RearTrainRearPosition, RearTrainFrontPosition, RearTrainExitPos;
5798  int FrontTrainRearPosition, FrontTrainFrontPosition, FrontTrainExitPos;
5800 
5801  // determine all positions & exits
5802  if(LocationName != "")
5803  {
5804  // if message given only call at ~5 sec intervals
5806  {
5807  FirstNamedElementPos = LeadElement;
5808  if(!Track->ThisNamedLocationLongEnoughForSplit(2, LocationName, FirstNamedElementPos, SecondNamedElementPos, FirstNamedLinkedElementPos,
5809  SecondNamedLinkedElementPos))
5810  {
5811  FirstNamedElementPos = MidElement;
5812  if(!Track->ThisNamedLocationLongEnoughForSplit(3, LocationName, FirstNamedElementPos, SecondNamedElementPos, FirstNamedLinkedElementPos,
5813  SecondNamedLinkedElementPos))
5814  {
5816  {
5817  TrainController->LogActionError(9, HeadCode, "", FailLocTooShort, LocationName);
5819  }
5820  Utilities->CallLogPop(1013);
5821  return;
5822  }
5823  }
5824  else
5825  {
5826  // if first is possible then check if all 4 positions at location, and if not try the second
5827  int LeadPosA = FirstNamedElementPos;
5828  int LeadPosB = FirstNamedLinkedElementPos;
5829  int LeadPosC = SecondNamedElementPos;
5830  int LeadPosD = SecondNamedLinkedElementPos;
5831  // count number of positions that are at the location
5832  int LeadNumAtLoc = 0;
5833  if(Track->TrackElementAt(767, LeadPosA).ActiveTrackElementName == LocationName)
5834  {
5835  LeadNumAtLoc++;
5836  }
5837  if(Track->TrackElementAt(768, LeadPosB).ActiveTrackElementName == LocationName)
5838  {
5839  LeadNumAtLoc++;
5840  }
5841  if(Track->TrackElementAt(769, LeadPosC).ActiveTrackElementName == LocationName)
5842  {
5843  LeadNumAtLoc++;
5844  }
5845  if(Track->TrackElementAt(770, LeadPosD).ActiveTrackElementName == LocationName)
5846  {
5847  LeadNumAtLoc++;
5848  }
5849  if(LeadNumAtLoc < 4)
5850  {
5851  FirstNamedElementPos = MidElement;
5852  if(!Track->ThisNamedLocationLongEnoughForSplit(5, LocationName, FirstNamedElementPos, SecondNamedElementPos, FirstNamedLinkedElementPos,
5853  SecondNamedLinkedElementPos)) // restore originals
5854  {
5855  FirstNamedElementPos = LeadPosA;
5856  FirstNamedLinkedElementPos = LeadPosB;
5857  SecondNamedElementPos = LeadPosC;
5858  SecondNamedLinkedElementPos = LeadPosD;
5859  }
5860  else // count number at location
5861  {
5862  int MidNumAtLoc = 0;
5863  if(Track->TrackElementAt(771, FirstNamedElementPos).ActiveTrackElementName == LocationName)
5864  {
5865  MidNumAtLoc++;
5866  }
5867  if(Track->TrackElementAt(772, FirstNamedLinkedElementPos).ActiveTrackElementName == LocationName)
5868  {
5869  MidNumAtLoc++;
5870  }
5871  if(Track->TrackElementAt(773, FirstNamedLinkedElementPos).ActiveTrackElementName == LocationName)
5872  {
5873  MidNumAtLoc++;
5874  }
5875  if(Track->TrackElementAt(774, SecondNamedLinkedElementPos).ActiveTrackElementName == LocationName)
5876  {
5877  MidNumAtLoc++;
5878  }
5879  if(LeadNumAtLoc > MidNumAtLoc)
5880  // change back, else keep new values
5881  {
5882  FirstNamedElementPos = LeadPosA;
5883  FirstNamedLinkedElementPos = LeadPosB;
5884  SecondNamedElementPos = LeadPosC;
5885  SecondNamedLinkedElementPos = LeadPosD;
5886  }
5887  }
5888  }
5889  }
5890  }
5891  else
5892  {
5893  Utilities->CallLogPop(1792);
5894  return;
5895  }
5896  }
5897  else
5898  {
5899  throw Exception("Error - LocationName not set in RearTrainSplit");
5900  }
5901  // set front & rear train parameters
5902  // need RearTrainRearPosition, RearTrainFrontPosition, RearTrainExitPos, FrontTrainRearPosition, FrontTrainFrontPosition & FrontTrainExitPos;
5903  // have LeadElement & MidElement of train defining its direction, & one or other on FirstNamedElementPos
5904  // have FirstNamedElementPos, SecondNamedElementPos, FirstNamedLinkedElementPos & SecondNamedLinkedElementPos from ThisNamedLocationLongEnoughForSplit.
5905  if(LeadElement == FirstNamedElementPos)
5906  {
5907  if(MidElement == SecondNamedElementPos)
5908  {
5909  FrontTrainFrontPosition = FirstNamedLinkedElementPos;
5910  FrontTrainRearPosition = FirstNamedElementPos;
5911  RearTrainFrontPosition = SecondNamedElementPos;
5912  RearTrainRearPosition = SecondNamedLinkedElementPos;
5913  }
5914  else // MidElement must == FirstNamedLinkedElementPos
5915  {
5916  FrontTrainFrontPosition = SecondNamedLinkedElementPos;
5917  FrontTrainRearPosition = SecondNamedElementPos;
5918  RearTrainFrontPosition = FirstNamedElementPos;
5919  RearTrainRearPosition = FirstNamedLinkedElementPos;
5920  }
5921  }
5922  else // MidElement == FirstNamedElementPos
5923  {
5924  if(LeadElement == SecondNamedElementPos)
5925  {
5926  FrontTrainFrontPosition = SecondNamedLinkedElementPos;
5927  FrontTrainRearPosition = SecondNamedElementPos;
5928  RearTrainFrontPosition = FirstNamedElementPos;
5929  RearTrainRearPosition = FirstNamedLinkedElementPos;
5930  }
5931  else // LeadElement must == FirstNamedLinkedElementPos
5932  {
5933  FrontTrainFrontPosition = FirstNamedLinkedElementPos;
5934  FrontTrainRearPosition = FirstNamedElementPos;
5935  RearTrainFrontPosition = SecondNamedElementPos;
5936  RearTrainRearPosition = SecondNamedLinkedElementPos;
5937  }
5938  }
5939  RearTrainExitPos = -1;
5940  for(int x = 0; x < 4; x++)
5941  {
5942  if(Track->TrackElementAt(588, RearTrainRearPosition).Conn[x] == RearTrainFrontPosition)
5943  {
5944  RearTrainExitPos = x;
5945  break;
5946  }
5947  }
5948  if(RearTrainExitPos == -1)
5949  {
5950  throw Exception("Error - RearTrainRearPosition not linked to RearTrainFrontPosition in RearTrainSplit");
5951  }
5952  FrontTrainExitPos = -1;
5953  for(int x = 0; x < 4; x++)
5954  {
5955  if(Track->TrackElementAt(589, FrontTrainRearPosition).Conn[x] == FrontTrainFrontPosition)
5956  {
5957  FrontTrainExitPos = x;
5958  break;
5959  }
5960  }
5961  if(FrontTrainExitPos == -1)
5962  {
5963  throw Exception("Error - FrontTrainRearPosition not linked to FrontTrainFrontPosition in RearTrainSplit");
5964  }
5965  // check no train (apart from self) on any of the 4 elements & fail if so
5966  int TrainIDOnRearOfRearTrain, TrainIDOnFrontOfRearTrain, TrainIDOnRearOfFrontTrain, TrainIDOnFrontOfFrontTrain;
5967  TTrackElement RearMostElement = Track->TrackElementAt(590, RearTrainRearPosition);
5968 
5969  if((RearMostElement.TrackType == Bridge) && (RearTrainExitPos > 1))
5970  {
5971  TrainIDOnRearOfRearTrain = RearMostElement.TrainIDOnBridgeTrackPos23;
5972  }
5973  else if((RearMostElement.TrackType == Bridge) && (RearTrainExitPos < 2))
5974  {
5975  TrainIDOnRearOfRearTrain = RearMostElement.TrainIDOnBridgeTrackPos01;
5976  }
5977  else
5978  {
5979  TrainIDOnRearOfRearTrain = RearMostElement.TrainIDOnElement;
5980  }
5981  // RearTrainFrontPosition = RearMostElement.Conn[RearTrainExitPos];
5982  TrainIDOnFrontOfRearTrain = Track->TrackElementAt(591, RearTrainFrontPosition).TrainIDOnElement;
5983  // can't be a bridge
5984  TrainIDOnRearOfFrontTrain = Track->TrackElementAt(592, FrontTrainRearPosition).TrainIDOnElement;
5985  // can't be a bridge
5986  // FrontTrainFrontPosition = Track->TrackElementAt(593,FrontTrainRearPosition).Conn[FrontTrainExitPos];
5987  TTrackElement FrontMostElement = Track->TrackElementAt(594, FrontTrainFrontPosition);
5988 
5989  if((FrontMostElement.TrackType == Bridge) && (FrontTrainExitPos > 1))
5990  {
5991  TrainIDOnFrontOfFrontTrain = FrontMostElement.TrainIDOnBridgeTrackPos23;
5992  }
5993  else if((FrontMostElement.TrackType == Bridge) && (FrontTrainExitPos < 2))
5994  {
5995  TrainIDOnFrontOfFrontTrain = FrontMostElement.TrainIDOnBridgeTrackPos01;
5996  }
5997  else
5998  {
5999  TrainIDOnFrontOfFrontTrain = FrontMostElement.TrainIDOnElement;
6000  }
6001  if(((TrainIDOnRearOfRearTrain > -1) && (TrainIDOnRearOfRearTrain != TrainID)) || ((TrainIDOnFrontOfRearTrain > -1) && (TrainIDOnFrontOfRearTrain != TrainID)
6002  ) || ((TrainIDOnRearOfFrontTrain > -1) && (TrainIDOnRearOfFrontTrain != TrainID)) ||
6003  ((TrainIDOnFrontOfFrontTrain > -1) && (TrainIDOnFrontOfFrontTrain != TrainID)))
6004  {
6006  {
6009  }
6010  // don't advance ActionVectorEntryPtr as need to keep trying, other train may move off eventually
6011  Utilities->CallLogPop(1014);
6012  return;
6013  }
6015  {
6017  }
6018  // reposition existing front train, need to do this first for 2 reasons - 1) will likely be in the way of the new rear train, and 2)
6019  // the new train will likely cause a reallocation of the TrainVector, and if so the reference to the existing train will be invalidated.
6020  // Hence deal with existing train while it references a valid entry in the vector, but retain the Old ActionVectorEntryPtr in a separate
6021  // variable as it is needed for setting up the new train
6022  TActionVectorEntry *OldActionVectorEntryPtr = ActionVectorEntryPtr;
6023 
6024  UnplotTrain(1);
6025  StartSpeed = 0;
6026  RearStartElement = FrontTrainRearPosition;
6027  RearStartExitPos = FrontTrainExitPos;
6028  StoppedAtLocation = true;
6029  if((PowerAtRail < 1) && EntrySpeed < 1) // added at v2.4.0
6030  {
6031  StoppedWithoutPower = true;
6032  }
6033  PlotStartPosition(4);
6038  Mass = Mass / 2;
6039  // TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).Mass = Mass;
6040  MaxBrakeRate = MaxBrakeRate / 2;
6041  // TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).MaxBrakeRate = MaxBrakeRate;
6042  PowerAtRail = PowerAtRail / 2;
6043  // TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).PowerAtRail = PowerAtRail;
6044  AValue = sqrt(2 * PowerAtRail / Mass);
6045  // shouldn't change but include in case not set earlier
6046 
6047  // create new rear train
6048 /*
6049  TrainController::AddTrain(int RearPosition, int FrontPosition, AnsiString HeadCode, int StartSpeed, int Mass,
6050  double MaxRunningSpeed, double MaxBrakeRate, double PowerAtRail, AnsiString ModeStr, TTrainDataEntry *TrainDataEntryPtr,
6051  int RepeatNumber, int IncrementalMinutes)
6052 */
6053  // same Mass, MaxBrakeRate & PowerAtRail as this train's halved values, and same MaxRunningSpeed as this train
6054  TActionEventType EventType = NoEvent;
6055 
6056  if(!TrainController->AddTrain(1, RearTrainRearPosition, RearTrainFrontPosition, OtherHeadCode, 0, Mass, MaxRunningSpeed, MaxBrakeRate, PowerAtRail,
6057  "Timetable", OldActionVectorEntryPtr->LinkedTrainEntryPtr, RepeatNumber, IncrementalMinutes, IncrementalDigits, SignallerMaxSpeed, false, EventType))
6058  // false for SignallerControl
6059  {
6060  Utilities->CallLogPop(1722); // EventType not used here
6061  // if fails either a throw will have been sent in AddTrain or start position failed prob because of
6062  // another train, in which case a message will have been sent to the perf log, also might well clear later
6063  // when other train moves away
6064  return;
6065  }
6066  // Note data in 'this' now probably invalid as there has been a new addition to the TrainVector, so the train is likely to have a new address, hence make no more changes for the current train
6067  // see mods in UpdateTrain for v1.3.2
6068  TrainController->TrainAdded = true;
6069  TTrainOperatingData &TTOD = OldActionVectorEntryPtr->LinkedTrainEntryPtr->TrainOperatingDataVector.at(RepeatNumber); // this is for the newly created train
6070 
6071  TTOD.TrainID = TrainController->TrainVector.back().TrainID;
6072  TTOD.RunningEntry = Running;
6073  Utilities->CallLogPop(1015);
6074 }
6075 
6076 // ---------------------------------------------------------------------------
6077 
6078 void TTrain::FinishJoin(int Caller)
6079 {
6080  if(FinishJoinLogSent == false)
6081  {
6082  TrainController->LogEvent("" + AnsiString(Caller) + ",FinishJoin" + "," + HeadCode);
6083  FinishJoinLogSent = true; // so don't keep logging this event
6084  // don't need to reset it to false after the event as the train is deleted
6085  }
6086  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",FinishJoin" + "," + HeadCode);
6087  if(TrainFailed)
6088  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can join when repaired) Can FinishJoin if zero power & not failed, as for empty stock
6089  {
6091  {
6092  TrainController->StopTTClockMessage(84, HeadCode + ": A failed train can't join another under timetable control");
6093  }
6095  Utilities->CallLogPop(2139);
6096  return;
6097  }
6098  if(TrainGone)
6099  // this means that the train has already joined the other & is awaiting deletion by TrainController
6100  // without this the 'waiting' message can be given since the other train's ActionVectorEntryPtr has moved
6101  // on from jbo & TrainToJoinIsAdjacent returns false
6102  {
6103  Utilities->CallLogPop(1035);
6104  return;
6105  }
6106  TTrain *TrainToJoin;
6108 
6109  if(!TrainToJoinIsAdjacent(0, TrainToJoin))
6110  {
6112  {
6113  // Display->PerformanceLog(2, TrainController->TTClockTime.FormatString("hh:nn:ss") + ": " + HeadCode + " waiting to join " + JBOHeadCode + " at " + ActionVectorEntryPtr->LocationName);
6116  }
6117  Utilities->CallLogPop(1030);
6118  return; // keep this here in case need to add code before final return
6119  }
6120  // no need to clear error report flag here, cleared in jbo function
6121  // No need to set TimetableFinished, done in jbo function
6122  Utilities->CallLogPop(1031);
6123 }
6124 
6125 // ---------------------------------------------------------------------------
6126 
6127 void TTrain::JoinedBy(int Caller)
6128 {
6129  TrainController->LogEvent("" + AnsiString(Caller) + ",JoinedBy" + "," + HeadCode);
6130  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",JoinedBy" + "," + HeadCode);
6131  if(PowerAtRail < 1)
6132  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can join when power restored)
6133  {
6135  {
6136  TrainController->StopTTClockMessage(85, HeadCode + ": A train without power can't be joined by another under timetable control");
6137  }
6139  Utilities->CallLogPop(2140);
6140  return;
6141  }
6142  TTrain *TrainToBeJoinedBy;
6144 
6145  if(!TrainToBeJoinedByIsAdjacent(0, TrainToBeJoinedBy))
6146  {
6148  {
6149  // Display->PerformanceLog(3, TrainController->TTClockTime.FormatString("hh:nn:ss") + ": " + HeadCode + " waiting to be joined by " + FJOHeadCode + " at " + ActionVectorEntryPtr->LocationName);
6152  }
6153  LastActionDelayFlag = true;
6154  // need to update LastActionTime if this train first to arrive as need 30s after
6155  // both adjacent before the join
6156  Utilities->CallLogPop(1032);
6157  return;
6158  }
6159  // here when other train is adjacent
6161  {
6163  // need to update this as need 30s after both adjacent before the join
6164  LastActionDelayFlag = false;
6165  Utilities->CallLogPop(1033);
6166  return;
6167  }
6168  // here when other train is adjacent & 30 secs elapsed since both adjacent
6169 
6170  // set new values for mass etc
6171  if(MaxRunningSpeed > TrainToBeJoinedBy->MaxRunningSpeed)
6172  {
6173  MaxRunningSpeed = TrainToBeJoinedBy->MaxRunningSpeed;
6174  }
6175  double OtherBrakeForce = TrainToBeJoinedBy->MaxBrakeRate * TrainToBeJoinedBy->Mass;
6176  double OwnBrakeForce = MaxBrakeRate * Mass;
6177  double CombinedBrakeRate = (OtherBrakeForce + OwnBrakeForce) / (TrainToBeJoinedBy->Mass + Mass);
6178 
6179  Mass += TrainToBeJoinedBy->Mass;
6180  MaxBrakeRate = CombinedBrakeRate;
6181  PowerAtRail += TrainToBeJoinedBy->PowerAtRail;
6182  AValue = sqrt(2 * PowerAtRail / Mass);
6183 
6185  TrainToBeJoinedBy->TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).EventReported = NoEvent;
6186  TrainToBeJoinedBy->TimetableFinished = true;
6187  TrainToBeJoinedBy->TrainGone = true;
6188  // this will cause other train to be deleted
6189  TrainToBeJoinedBy->JoinedOtherTrainFlag = true;
6193  Utilities->CallLogPop(1034);
6194 }
6195 
6196 // ---------------------------------------------------------------------------
6197 
6199 {
6200  TrainController->LogEvent("" + AnsiString(Caller) + ",ChangeTrainDirection" + "," + HeadCode);
6201  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ChangeTrainDirection" + "," + HeadCode);
6202  if(PowerAtRail < 1)
6203  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can change direction when power restored)
6204  {
6206  {
6207  TrainController->StopTTClockMessage(86, HeadCode + ": A train without power can't change direction under timetable control");
6208  }
6209  ZeroPowerNoCDTMessage = true;
6210  Utilities->CallLogPop(2141);
6211  return;
6212  }
6213  TColor TempColour = BackgroundColour;
6214 
6215  UnplotTrain(2);
6218  StartSpeed = 0;
6219  StoppedAtLocation = true;
6220  PlotStartPosition(1);
6221  PlotTrainWithNewBackgroundColour(27, TempColour, Display);
6222  // plot same as was - should always be pale green
6226 
6227  //now erase a stub route if there is one, added at v2.5.1
6228  //first element of route is now immediately behind the train (i.e. next to MidElement)
6229  if(MidEntryPos >= 0)
6230  {
6231  TTrackElement MidTrackElement = Track->TrackElementAt(996, MidElement);
6232  int FirstRouteElementVecPos = MidTrackElement.Conn[MidEntryPos];
6233  int FirstRouteLinkPos = MidTrackElement.ConnLinkPos[MidEntryPos];
6234  int RouteNumber = -1;
6235  TAllRoutes::TRouteType RouteType = AllRoutes->GetRouteTypeAndNumber(34, FirstRouteElementVecPos, FirstRouteLinkPos, RouteNumber);
6236  if(RouteType == TAllRoutes::NotAutoSigsRoute)
6237  {
6238  TOneRoute &OR = AllRoutes->GetModifiableRouteAt(28, RouteNumber);
6239  TTrackElement TE = Track->TrackElementAt(997, FirstRouteElementVecPos);
6240  if((TE.TrackType != SignalPost) && (TE.TrackType != Continuation)) //all autosigs routes have signalpost or continuation at 0 so they are automatically excluded
6241  {
6242  bool FirstPass = true; //added at v2.8.0
6243  while(OR.PrefDirSize() > 0) //remove the route up to but not including the next facing signal, in case a pref dir route extends to another signal
6244  {
6245  TPrefDirElement PDE = OR.GetFixedPrefDirElementAt(247, 0); //these will change at each element removal because OR is a reference to the real route
6246  int TVPos2 = PDE.GetTrackVectorPosition();
6247  if(FirstPass && (TVPos2 != FirstRouteElementVecPos)) //route is not directed away from cdt train, could be a call-on for another train (added at v2.8.0)
6248  {
6249  break;
6250  }
6251  TTrackElement TE2 = Track->TrackElementAt(998, TVPos2);
6253  {
6254  AllRoutes->RemoveRouteElement(22, TE2.HLoc, TE2.VLoc, PDE.GetELink());
6255  }
6256  else
6257  {
6258  break;
6259  }
6260  FirstPass = false;
6261  }
6262  AllRoutes->RebuildRailwayFlag = true;
6263  // to force ClearandRebuildRailway at next clock tick if not in zoom-out mode, to replot without stub route
6264  }
6265  }
6266  }
6267  Utilities->CallLogPop(1012);
6268 }
6269 
6270 // ---------------------------------------------------------------------------
6271 
6272 void TTrain::NewTrainService(int Caller)
6273 // change to new train, give new service message
6274 {
6275  TrainController->LogEvent("" + AnsiString(Caller) + ",NewTrainService" + "," + HeadCode);
6276  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",NewTrainService" + "," + HeadCode);
6277  if(PowerAtRail < 1)
6278  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can form new service when power restored)
6279  {
6281  {
6282  TrainController->StopTTClockMessage(87, HeadCode + ": A train without power can't form a new service");
6283  }
6285  Utilities->CallLogPop(2142);
6286  return;
6287  }
6289 
6291  UnplotTrain(3);
6294  StartSpeed = 0;
6299  HeadCode = NewHeadCode;
6300  StoppedAtLocation = true;
6301  PlotStartPosition(5);
6303  // pale green
6306  TerminatedMessageSent = false;
6307  Utilities->CallLogPop(1022);
6308 }
6309 
6310 // ---------------------------------------------------------------------------
6311 
6312 void TTrain::RemainHere(int Caller)
6313 {
6314  Utilities->CallLog.push_back(Utilities->TimeStamp() + AnsiString(Caller) + ",RemainHere" + "," + HeadCode);
6315  if(RemainHereLogNotSent) // to prevent repeated logs
6316  {
6317  TrainController->LogEvent(Utilities->TimeStamp() + AnsiString(Caller) + ",RemainHere" + "," + HeadCode);
6318  RemainHereLogNotSent = false;
6319  }
6321  {
6322  Display->PerformanceLog(5, Utilities->Format96HHMMSS(TrainController->TTClockTime) + ": " + HeadCode + " terminated at " +
6325  TerminatedMessageSent = true;
6326  }
6327  TimetableFinished = true;
6328  Utilities->CallLogPop(1023);
6329 }
6330 
6331 // ---------------------------------------------------------------------------
6332 
6333 void TTrain::SendMissedActionLogs(int Caller, int IncNum, TActionVectorEntry *Ptr)
6334 /*
6335  Enter with pointer at next expected action, and IncNum the number by which have to increase the pointer
6336  to reach the action that is valid for the train's current position. i.e. IncNum error messages to be sent
6337  except where an action is a departure, starting at the current value for the pointer
6338  If IncNum is -1, then send messages for all remaining actions, including Fer if present
6339  If IncNum is -2, then send messages for all remaining actions, except Fer if present
6340 */{
6341  if((Ptr->Command == "Snt") && Ptr->SignallerControl)
6342  {
6343  return; // if remove train that starts under signaller control no messages needed
6344 
6345  }
6346  TrainController->LogEvent("" + AnsiString(Caller) + ",SendMissedActionLogs," + AnsiString(IncNum) + "," + HeadCode);
6347  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SendMissedActionLogs," + AnsiString(IncNum) + "," + HeadCode);
6348  if(IncNum > 0)
6349  {
6350  for(int x = 0; x < IncNum; x++)
6351  {
6352  if(x > 0)
6353  {
6354  Ptr++;
6355  }
6356  // arrival - no need to test for termination as this section only covers missed actions up to the
6357  // arrival point - may terminate later but that not missed
6358  if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime > TDateTime(-1)))
6359  {
6361  }
6362  // arrival & departure
6363  if(Ptr->FormatType == TimeTimeLoc)
6364  {
6366  }
6367  // departure
6368  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime == TDateTime(-1)))
6369  {
6370  continue; // skip TimeLoc departures, message given for arrivals
6371  }
6372  // pass
6373  else if(Ptr->FormatType == PassTime)
6374  {
6376  }
6377  // split
6378  else if((Ptr->Command == "fsp") || (Ptr->Command == "rsp"))
6379  {
6381  }
6382  // jbo
6383  else if(Ptr->Command == "jbo")
6384  {
6386  }
6387  // Errors - have reached a station stop point (before a cdt) during Train->Update() so intervening actions can't
6388  // be starts, finishes or cdt
6389  else if((Ptr->Command == "Fns") || (Ptr->Command == "Frh") || (Ptr->Command == "Fer") || (Ptr->Command == "Fjo") || (Ptr->Command == "Snt") ||
6390  (Ptr->Command == "Sfs") || (Ptr->Command == "Snt-sh") || (Ptr->Command == "Sns") || (Ptr->Command == "Sns-sh") || (Ptr->Command == "Sns-fsh") ||
6391  (Ptr->Command == "cdt") || (Ptr->Command == "Frh-sh") || (Ptr->Command == "Fns-sh") || (Ptr->Command == "F-nshs") ||
6392  (Ptr->FormatType == Repeat))
6393  {
6394  throw Exception("Error - illegal command in SendMissedActionLogs for IncNum = " + AnsiString(IncNum) + ", and command = " + Ptr->Command);
6395  }
6396  }
6397  }
6398  else
6399  {
6400  bool IncludeFER = false;
6401  if(IncNum == -1)
6402  {
6403  IncludeFER = true;
6404  }
6405  while(true) // finish commands & repeats break out of loop
6406  {
6407  // Fer & excluded - send normal exit log to give minutes late or early - no, have already sent an unexpected exit message
6408  if(!IncludeFER && (Ptr->Command == "Fer"))
6409  {
6410  break;
6411  }
6412  // Fer & included
6413  else if(IncludeFER && (Ptr->Command == "Fer"))
6414  {
6416  break;
6417  }
6418  // Repeat
6419  else if(Ptr->FormatType == Repeat)
6420  {
6421  break;
6422  }
6423  // Fjo
6424  else if(Ptr->Command == "Fjo")
6425  {
6427  break;
6428  }
6429  // Frh
6430  else if(Ptr->Command == "Frh")
6431  {
6433  {
6435  TerminatedMessageSent = true;
6436  }
6437  break;
6438  }
6439  // Frh-sh
6440  else if(Ptr->Command == "Frh-sh")
6441  {
6443  {
6445  TerminatedMessageSent = true;
6446  }
6447  break;
6448  }
6449  // Fns, F-nshs, Fns-sh
6450  else if((Ptr->Command == "Fns") || (Ptr->Command == "F-nshs") || (Ptr->Command == "Fns-sh"))
6451  {
6453  break;
6454  }
6455  // end of breakout actions
6456  // arrival
6457  if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime > TDateTime(-1)))
6458  {
6459  if(IsTrainTerminating(1))
6460  {
6462  TerminatedMessageSent = true;
6463  }
6464  else
6465  {
6467  }
6468  }
6469  // arrival & departure
6470  else if(Ptr->FormatType == TimeTimeLoc)
6471  {
6473  }
6474  // departure
6475  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime == TDateTime(-1)))
6476  {
6477  Ptr++;
6478  continue; // skip TimeLoc departures, message given for arrivals
6479  }
6480  // pass
6481  else if(Ptr->FormatType == PassTime)
6482  {
6484  }
6485  // split
6486  else if((Ptr->Command == "fsp") || (Ptr->Command == "rsp"))
6487  {
6489  }
6490  // jbo
6491  else if(Ptr->Command == "jbo")
6492  {
6494  }
6495  // cdt
6496  else if(Ptr->Command == "cdt")
6497  {
6499  }
6500  // Errors
6501  else if((Ptr->Command == "Snt-sh") || (Ptr->Command == "Sfs") || (Ptr->Command == "Sns") || (Ptr->Command == "Sns-sh") ||
6502  (Ptr->Command == "Sns-fsh") || ((Ptr->Command == "Snt") && !Ptr->SignallerControl))
6503  {
6504  throw Exception("Error - illegal command in SendMissedActionLogs for IncNum = " + AnsiString(IncNum) + ", and command = " + Ptr->Command);
6505  }
6506  Ptr++;
6507  }
6508  TimetableFinished = true;
6509  }
6510  Utilities->CallLogPop(1021);
6511 }
6512 
6513 // ---------------------------------------------------------------------------
6514 
6515 bool TTrain::TrainToJoinIsAdjacent(int Caller, TTrain* &TrainToJoin)
6516 // ensure same repeatnumber
6517 {
6518  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TrainToJoinIsAdjacent" + "," + HeadCode);
6520 
6521  if(TrainToJoinTDEntry->TrainOperatingDataVector.at(RepeatNumber).RunningEntry != Running)
6522  {
6523  Utilities->CallLogPop(1024);
6524  return(false);
6525  }
6526  TrainToJoin = &(TrainController->TrainVectorAtIdent(33, TrainToJoinTDEntry->TrainOperatingDataVector.at(RepeatNumber).TrainID));
6527  if(TrainToJoin->StoppedAtLocation && (TrainToJoin->TrainMode == Timetable) && (TrainToJoin->ActionVectorEntryPtr->Command == "jbo"))
6528  {
6529  if((Track->TrackElementAt(610, LeadElement).Conn[LeadExitPos] == TrainToJoin->LeadElement) || (Track->TrackElementAt(611,
6530  LeadElement).Conn[LeadExitPos] == TrainToJoin->MidElement) || (Track->TrackElementAt(612, MidElement).Conn[MidEntryPos] == TrainToJoin->LeadElement)
6531  || (Track->TrackElementAt(613, MidElement).Conn[MidEntryPos] == TrainToJoin->MidElement))
6532  {
6533  Utilities->CallLogPop(1025);
6534  return(true);
6535  }
6536  }
6537  Utilities->CallLogPop(1026);
6538  return(false);
6539 }
6540 
6541 // ---------------------------------------------------------------------------
6542 
6543 bool TTrain::TrainToBeJoinedByIsAdjacent(int Caller, TTrain* &TrainToBeJoinedBy)
6544 // ensure same repeatnumber
6545 {
6546  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TrainToBeJoinedByIsAdjacent" + "," + HeadCode);
6547  TTrainDataEntry *TrainToBeJoinedByTDEntry = ActionVectorEntryPtr->LinkedTrainEntryPtr;
6548 
6549  if(TrainToBeJoinedByTDEntry->TrainOperatingDataVector.at(RepeatNumber).RunningEntry != Running)
6550  {
6551  Utilities->CallLogPop(1027);
6552  return(false);
6553  }
6554  TrainToBeJoinedBy = &(TrainController->TrainVectorAtIdent(15, TrainToBeJoinedByTDEntry->TrainOperatingDataVector.at(RepeatNumber).TrainID));
6555  if(TrainToBeJoinedBy->StoppedAtLocation && (TrainToBeJoinedBy->TrainMode == Timetable) && (TrainToBeJoinedBy->ActionVectorEntryPtr->Command == "Fjo"))
6556  {
6557  if((Track->TrackElementAt(614, LeadElement).Conn[LeadExitPos] == TrainToBeJoinedBy->LeadElement) || (Track->TrackElementAt(615,
6558  LeadElement).Conn[LeadExitPos] == TrainToBeJoinedBy->MidElement) || (Track->TrackElementAt(616,
6559  MidElement).Conn[MidEntryPos] == TrainToBeJoinedBy->LeadElement) || (Track->TrackElementAt(617,
6560  MidElement).Conn[MidEntryPos] == TrainToBeJoinedBy->MidElement))
6561  {
6562  Utilities->CallLogPop(1028);
6563  return(true);
6564  }
6565  }
6566  Utilities->CallLogPop(1029);
6567  return(false);
6568 }
6569 
6570 // ---------------------------------------------------------------------------
6571 
6573 {
6574  TrainController->LogEvent("" + AnsiString(Caller) + ",NewShuttleFromNonRepeatService" + "," + HeadCode);
6575  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",NewShuttleFromNonRepeatService" + "," + HeadCode);
6576  if(PowerAtRail < 1)
6577  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can for new service when power restored)
6578  {
6580  {
6581  TrainController->StopTTClockMessage(88, HeadCode + ": A train without power can't form a new service");
6582  }
6584  Utilities->CallLogPop(2143);
6585  return;
6586  }
6587  AnsiString NewHeadCode = ActionVectorEntryPtr->NonRepeatingShuttleLinkHeadCode;
6588 
6590  UnplotTrain(4);
6593  StartSpeed = 0;
6598  HeadCode = NewHeadCode;
6599  IncrementalMinutes = TrainDataEntryPtr->ActionVector.back().RearStartOrRepeatMins;
6600  IncrementalDigits = TrainDataEntryPtr->ActionVector.back().FrontStartOrRepeatDigits;
6601  StoppedAtLocation = true;
6602  PlotStartPosition(6);
6604  // pale green
6607  TerminatedMessageSent = false;
6608  Utilities->CallLogPop(1078);
6609 }
6610 
6611 // ---------------------------------------------------------------------------
6612 
6614 // need to check whether all repeats finished or not
6615 {
6616  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",RepeatShuttleOrRemainHere" + "," + HeadCode);
6617  if(RemainHereLogNotSent) // to prevent repeated logs
6618  {
6619  TrainController->LogEvent("" + AnsiString(Caller) + ",RepeatShuttleOrRemainHere" + "," + HeadCode);
6620  RemainHereLogNotSent = false;
6621  }
6623  // finished all repeats
6624  {
6626  {
6629  TerminatedMessageSent = true;
6630  // no need to clear message as no more actions
6631  }
6632  TimetableFinished = true;
6633  Utilities->CallLogPop(1080);
6634  return;
6635  }
6636  if(PowerAtRail < 1)
6637  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can for new service when power restored)
6638  {
6640  {
6641  TrainController->StopTTClockMessage(89, HeadCode + ": A train without power can't form a new service");
6642  }
6644  Utilities->CallLogPop(2144);
6645  return;
6646  }
6647  int TempRepeatNumber = RepeatNumber + 1;
6648  // need the next repeat value in order to obtain a correct NewHeadCode, but don't increase it
6649  // until after LogAction or the wrong time will be used
6650  AnsiString NewHeadCode = TrainController->GetRepeatHeadCode(6, ActionVectorEntryPtr->OtherHeadCode, TempRepeatNumber, IncrementalDigits);
6651 
6653  RepeatNumber++;
6654  UnplotTrain(5);
6657  StartSpeed = 0;
6662  HeadCode = NewHeadCode;
6663  StoppedAtLocation = true;
6664  PlotStartPosition(7);
6666  // pale green
6669  TerminatedMessageSent = false;
6670  Utilities->CallLogPop(1079);
6671 }
6672 
6673 // ---------------------------------------------------------------------------
6674 
6676 {
6677  TrainController->LogEvent("" + AnsiString(Caller) + ",RepeatShuttleOrNewNonRepeatService" + "," + HeadCode);
6678  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",RepeatShuttleOrNewNonRepeatService" + "," + HeadCode);
6679  if(PowerAtRail < 1)
6680  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can for new service when power restored)
6681  {
6683  {
6684  TrainController->StopTTClockMessage(90, HeadCode + ": A train without power can't form a new service");
6685  }
6687  Utilities->CallLogPop(2145);
6688  return;
6689  }
6691  // finished all repeats
6692  {
6693  AnsiString NewHeadCode = ActionVectorEntryPtr->NonRepeatingShuttleLinkHeadCode;
6695  RepeatNumber = 0;
6696  UnplotTrain(6);
6699  StartSpeed = 0;
6701  TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).TrainID = TrainID; // but note that RepeatNumber = 0
6702  TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).RunningEntry = Running; // but note that RepeatNumber = 0
6704  HeadCode = NewHeadCode;
6705  StoppedAtLocation = true;
6706  PlotStartPosition(9);
6710  TerminatedMessageSent = false;
6711  Utilities->CallLogPop(1081);
6712  return;
6713  }
6714  int TempRepeatNumber = RepeatNumber + 1;
6715  // need the next repeat value in order to obtain a correct NewHeadCode, but don't increase it
6716  // until after LogAction or the wrong time will be used
6717  AnsiString NewHeadCode = TrainController->GetRepeatHeadCode(7, ActionVectorEntryPtr->OtherHeadCode, TempRepeatNumber, IncrementalDigits);
6718 
6720  RepeatNumber++;
6721  UnplotTrain(7);
6724  StartSpeed = 0;
6729  HeadCode = NewHeadCode;
6730  StoppedAtLocation = true;
6731  PlotStartPosition(8);
6733  // pale green
6736  TerminatedMessageSent = false;
6737  Utilities->CallLogPop(1082);
6738 }
6739 
6740 // ---------------------------------------------------------------------------
6741 
6743 {
6744  // Search ActionVector from the position after the entry value for Ptr to the end, and return true if find a Finish
6745  // entry before Fer or TimeLoc. No point checking for TimeTimeLoc since at a stop location now so a later TimeTimeLoc
6746  // must be preceded by a TimeLoc departure
6747  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",IsTrainTerminating" + "," + HeadCode);
6748  for(unsigned int x = 1; x < TrainDataEntryPtr->ActionVector.size(); x++)
6749  {
6751  {
6752  if(((ActionVectorEntryPtr + x)->Command == "Fer") || ((ActionVectorEntryPtr + x)->FormatType == TimeLoc))
6753  {
6754  Utilities->CallLogPop(1083);
6755  return(false);
6756  }
6757  else if((ActionVectorEntryPtr + x)->SequenceType == Finish)
6758  {
6759  Utilities->CallLogPop(1084);
6760  return(true);
6761  }
6762  }
6763  }
6764  Utilities->CallLogPop(1085);
6765  return(false);
6766 }
6767 
6768 // ---------------------------------------------------------------------------
6769 
6770 bool TTrain::AbleToMove(int Caller)
6771 {
6772  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",AbleToMove" + "," + HeadCode);
6773  bool Able = true;
6774 
6775  if(Crashed || Derailed || StoppedAtBuffers || StoppedAtSignal || StoppedWithoutPower) // StoppedWithoutPower added at v2.4.0 &
6776  {
6777  // StoppedForTrainInFront removed as tested below
6778  Able = false;
6779  Utilities->CallLogPop(2146); // added v2.4.0
6780  return(Able); // added v2.4.0
6781  }
6782  if(LeadElement > -1)
6783  {
6784  int FrontPos = Track->TrackElementAt(678, LeadElement).Conn[LeadExitPos];
6785  int FrontEntryPos = Track->TrackElementAt(679, LeadElement).ConnLinkPos[LeadExitPos];
6786  if((FrontPos > -1) && (TrainMode == Signaller) && StoppedForTrainInFront)
6787  {
6788  TTrackElement TrackElement = Track->TrackElementAt(680, FrontPos);
6789  if((TrackElement.TrackType != Bridge) && (TrackElement.TrainIDOnElement == -1))
6790  {
6791  Able = true;
6792  StoppedForTrainInFront = false;
6793  }
6794  else if((TrackElement.TrackType == Bridge) && (FrontEntryPos < 2) && (TrackElement.TrainIDOnBridgeTrackPos01 == -1))
6795  {
6796  Able = true;
6797  StoppedForTrainInFront = false;
6798  }
6799  else if((TrackElement.TrackType == Bridge) && (FrontEntryPos > 1) && (TrackElement.TrainIDOnBridgeTrackPos23 == -1))
6800  {
6801  Able = true;
6802  StoppedForTrainInFront = false;
6803  }
6804  }
6805  else
6806  {
6808  {
6809  Able = false;
6810  }
6811  // don't set StoppedAtBuffers as (presumably) StoppedAtLocation & leave it at that
6812  }
6813  }
6814  else // leaving at a continuation so keep going
6815  {
6816  Able = true;
6817  StoppedForTrainInFront = false;
6818  }
6819  Utilities->CallLogPop(1454);
6820  return(Able);
6821 }
6822 
6823 // ---------------------------------------------------------------------------
6824 
6826 {
6827  // first check if a train immediately in front (may have moved there since this train stopped so StoppedForTrainInFront
6828  // won't be set; if there is a train then set StoppedForTrainInFront
6829  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",AbleToMoveButForSignal" + "," + HeadCode);
6830  // addition below for v1.3.2 after Carwyn Thomas fault reported 24/05/15 - need to check if exiting at continuation (LeadElement == -1) as if so fails at VecPos = .....
6831  if(LeadElement == -1) // exiting at continuation
6832  {
6833  Utilities->CallLogPop(2045);
6834  return(false);
6835  }
6836  // end of addition
6837  int VecPos = Track->TrackElementAt(654, LeadElement).Conn[LeadExitPos];
6838  int NextEntryPos = Track->TrackElementAt(655, LeadElement).ConnLinkPos[LeadExitPos];
6839 
6840  if(Track->OtherTrainOnTrack(5, VecPos, NextEntryPos, TrainID))
6841  {
6842  StoppedForTrainInFront = true;
6843  Utilities->CallLogPop(1455);
6844  return(false);
6845  }
6846  else
6847  {
6848  Utilities->CallLogPop(1456);
6850  // StoppedWithoutPower added v2.4.0
6851  }
6852 }
6853 
6854 // ---------------------------------------------------------------------------
6855 
6857 {
6858  // unplots & replots train, which checks for facing signal and sets StoppedAtSignal if req'd
6859  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SignallerChangeTrainDirection" + "," + HeadCode);
6860  TColor TempColour = BackgroundColour;
6861 
6862  UnplotTrain(8);
6865  StartSpeed = 0;
6866  PlotStartPosition(2);
6867  PlotTrainWithNewBackgroundColour(26, TempColour, Display);
6868 
6869  //now erase a stub route if there is one, added at v2.5.1
6870  //first element of route is now immediately behind the train (i.e. next to MidElement)
6871  if(MidEntryPos >= 0)
6872  {
6873  TTrackElement MidTrackElement = Track->TrackElementAt(1000, MidElement);
6874  int FirstRouteElementVecPos = MidTrackElement.Conn[MidEntryPos];
6875  int FirstRouteLinkPos = MidTrackElement.ConnLinkPos[MidEntryPos];
6876  int RouteNumber = -1;
6877  TAllRoutes::TRouteType RouteType = AllRoutes->GetRouteTypeAndNumber(35, FirstRouteElementVecPos, FirstRouteLinkPos, RouteNumber);
6878  if(RouteType == TAllRoutes::NotAutoSigsRoute)
6879  {
6880  TOneRoute &OR = AllRoutes->GetModifiableRouteAt(29, RouteNumber);
6881  TTrackElement TE = Track->TrackElementAt(1001, FirstRouteElementVecPos);
6882  if((TE.TrackType != SignalPost) && (TE.TrackType != Continuation)) //all autosigs routes have signalpost or continuation at 0 so they are automatically excluded
6883  {
6884  bool FirstPass = true; //added at v2.8.0
6885  while(OR.PrefDirSize() > 0) //remove the route up to but not including the next facing signal, in case a route extends to another signal
6886  {
6887  TPrefDirElement PDE = OR.GetFixedPrefDirElementAt(248, 0); //these will change at each element removal because OR is a reference to the real route
6888  int TVPos2 = PDE.GetTrackVectorPosition();
6889  if(FirstPass && (TVPos2 != FirstRouteElementVecPos)) //route is not directed away from cdt train, could be a call-on for another train (added at v2.8.0)
6890  {
6891  break;
6892  }
6893  TTrackElement TE2 = Track->TrackElementAt(1002, TVPos2);
6895  {
6896  AllRoutes->RemoveRouteElement(23, TE2.HLoc, TE2.VLoc, PDE.GetELink());
6897  }
6898  else
6899  {
6900  break;
6901  }
6902  FirstPass = false;
6903  }
6904  AllRoutes->RebuildRailwayFlag = true;
6905  // to force ClearandRebuildRailway at next clock tick if not in zoom-out mode, to replot without stub route
6906  }
6907  }
6908  }
6909  Utilities->CallLogPop(1102);
6910 }
6911 
6912 // ---------------------------------------------------------------------------
6913 
6915 {
6916  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + "," + AnsiString(Ptr - &TrainDataEntryPtr->ActionVector.front()) +
6917  ",FloatingLabelNextString" + "," + HeadCode);
6918  AnsiString RetStr = "", LocationName = "";
6919 
6920  if((Ptr->Command != "") && (Ptr->Command[1] == 'S'))
6921  {
6922  throw Exception("Error - start entry in FloatingLabelNextString");
6923  }
6924  if(Ptr->FormatType == TimeTimeLoc)
6925  {
6926  if(TrainMode == Timetable)
6927  {
6928  if(!TrainAtLocation(0, LocationName) || (LocationName != Ptr->LocationName))
6929  // not arrived yet in tt mode
6930  {
6931  RetStr = "Arrive " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(2, Ptr->ArrivalTime));
6932  }
6933  else
6934  {
6935  RetStr = "Depart " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(3, Ptr->DepartureTime));
6936  }
6937  }
6938  else // TrainMode == Signaller
6939  {
6940  if(!DepartureTimeSet) // not arrived yet
6941  {
6942  RetStr = "Arrive " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(45, Ptr->ArrivalTime));
6943  }
6944  else
6945  {
6946  RetStr = "Depart " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(36, Ptr->DepartureTime));
6947  }
6948  }
6949  }
6950  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime != TDateTime(-1)))
6951  {
6952  RetStr = "Arrive " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(4, Ptr->ArrivalTime));
6953  }
6954  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime == TDateTime(-1)))
6955  {
6956  RetStr = "Depart " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(5, Ptr->DepartureTime));
6957  }
6958  else if(Ptr->FormatType == PassTime) // new
6959  {
6960  RetStr = "Pass " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(31, Ptr->EventTime));
6961  }
6962  else if(Ptr->Command == "Fns")
6963  {
6964  RetStr = "Forms new service " + TrainController->GetRepeatHeadCode(8, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " +
6965  Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(6, Ptr->EventTime));
6966  RetStr = CheckNewServiceDepartureTime(0, Ptr, RepeatNumber, Ptr->LinkedTrainEntryPtr, RetStr); //if there is a next service this adds the new service departure time to RetStr
6967  }
6968  else if(Ptr->Command == "F-nshs")
6969  {
6970  RetStr = "Forms new service " + Ptr->NonRepeatingShuttleLinkHeadCode + " at " + Ptr->LocationName + " at " +
6972  RetStr = CheckNewServiceDepartureTime(1, Ptr, 0, Ptr->LinkedTrainEntryPtr, RetStr); //if there is a next service this adds the new service departure time to RetStr
6973  //note that use LinkedTrainEntryPtr and not NonRepeatingShuttleLinkEntryPtr because the forward link from the feeder is LinkedTrainEntryPtr.
6974  //NonRepeatingShuttleLinkEntryPtr is in the shuttle's ActionVector to point back to the feeder.
6975  //NonRepeatingShuttleLinkEntryPtr is used below from the last shuttle as the forward link to the finishing service
6976  }
6977  else if((Ptr->Command == "Fns-sh") && (RepeatNumber < (TrainDataEntryPtr->NumberOfTrains - 1))) // not last repeat number
6978  {
6979  RetStr = "Forms new service " + TrainController->GetRepeatHeadCode(9, Ptr->OtherHeadCode, RepeatNumber + 1, IncrementalDigits) + " at " +
6980  Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(7, Ptr->EventTime));
6981  // use RepeatNumber+1 as it's the repeat number of the NEXT shuttle service that is relevant
6982  RetStr = CheckNewServiceDepartureTime(2, Ptr, RepeatNumber + 1, Ptr->LinkedTrainEntryPtr, RetStr); //if there is a next service this adds the new service departure time to RetStr
6983  }
6984  else if((Ptr->Command == "Fns-sh") && (RepeatNumber >= (TrainDataEntryPtr->NumberOfTrains - 1))) // last repeat number
6985  {
6986  RetStr = "Forms new service " + Ptr->NonRepeatingShuttleLinkHeadCode,
6987  +" at " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(8, Ptr->EventTime));
6988  RetStr = CheckNewServiceDepartureTime(3, Ptr, 0, Ptr->NonRepeatingShuttleLinkEntryPtr, RetStr); //if there is a next service this adds the new service departure time to RetStr
6989  }
6990  else if((Ptr->Command == "Frh-sh") && (RepeatNumber < (TrainDataEntryPtr->NumberOfTrains - 1))) // not last repeat number
6991  {
6992  RetStr = "Forms new service " + TrainController->GetRepeatHeadCode(10, Ptr->OtherHeadCode, RepeatNumber + 1, IncrementalDigits) + " at " +
6993  Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(9, Ptr->EventTime));
6994  // use RepeatNumber+1 as it's the repeat number of the NEXT shuttle service that is relevant
6995  RetStr = CheckNewServiceDepartureTime(4, Ptr, RepeatNumber + 1, Ptr->LinkedTrainEntryPtr, RetStr); //if there is a next service this adds the new service departure time to RetStr
6996  }
6997  else if((Ptr->Command == "Frh-sh") && (RepeatNumber >= (TrainDataEntryPtr->NumberOfTrains - 1))) // last repeat number
6998  {
6999  RetStr ="None, train terminated at " + Ptr->LocationName;
7000  }
7001  else if(Ptr->Command == "Frh")
7002  {
7003  RetStr = "None, train terminated at " + Ptr->LocationName;
7004  }
7005  else if(Ptr->Command == "Fer")
7006  {
7007  AnsiString AllowedExits = "";
7008  RetStr = "Exit railway" + TrainController->GetExitLocationAndAt(1, Ptr->ExitList, AllowedExits) + " at " + Utilities->Format96HHMM(GetTrainTime(10, Ptr->EventTime)) + AllowedExits;
7009  }
7010  else if(Ptr->Command == "Fjo")
7011  {
7012  RetStr = "Join " + TrainController->GetRepeatHeadCode(11, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName + " at " +
7014  }
7015  else if(Ptr->Command == "jbo")
7016  {
7017  RetStr = "Joined by " + TrainController->GetRepeatHeadCode(12, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName +
7018  " at " + Utilities->Format96HHMM(GetTrainTime(12, Ptr->EventTime));
7019  }
7020  else if(Ptr->Command == "fsp")
7021  {
7022  RetStr = "Front split to " + TrainController->GetRepeatHeadCode(13, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName +
7023  " at " + Utilities->Format96HHMM(GetTrainTime(13, Ptr->EventTime));
7024  }
7025  else if(Ptr->Command == "rsp")
7026  {
7027  RetStr = "Rear split to " + TrainController->GetRepeatHeadCode(14, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName +
7028  " at " + Utilities->Format96HHMM(GetTrainTime(14, Ptr->EventTime));
7029  }
7030  else if(Ptr->Command == "cdt")
7031  {
7032  RetStr = "Change direction at " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(15, Ptr->EventTime));
7033  }
7034  Utilities->CallLogPop(1124);
7035  return(RetStr);
7036 }
7037 
7038 // ---------------------------------------------------------------------------
7039 
7040 AnsiString TTrain::CheckNewServiceDepartureTime(int Caller, TActionVectorEntry *Ptr, int RptNum, TTrainDataEntry *LinkedTrainDataPtr, AnsiString RetStr)
7041 {
7042  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + "," + AnsiString(Ptr - &TrainDataEntryPtr->ActionVector.front()) + ","
7043  + AnsiString(RptNum) + ",CheckNewServiceDepartureTime," + HeadCode);
7044  AnsiString DepTime = "", EventTime = "";
7045  bool CDTFlag = false; //reports if train changes direction before departs
7046  TActionVector NewServiceAV = LinkedTrainDataPtr->ActionVector;
7047  for(TActionVectorIterator AVI = NewServiceAV.begin(); AVI < NewServiceAV.end(); AVI++)
7048  {
7049  if(AVI->Command == "cdt")
7050  {
7051  CDTFlag = !CDTFlag; //toggles flag - allows for there being more than one cdt before departure
7052  continue;
7053  }
7054  if((AVI->Command == "fsp") || (AVI->Command == "rsp"))
7055  {
7056  EventTime = Utilities->Format96HHMM(TrainController->GetControllerTrainTime(19, AVI->EventTime, RptNum, IncrementalMinutes));
7057  RetStr += "\nNew service splits at " + EventTime;
7058  Utilities->CallLogPop(2234);
7059  return(RetStr);
7060  }
7061  if(AVI->Command == "jbo")
7062  {
7063  EventTime = Utilities->Format96HHMM(TrainController->GetControllerTrainTime(20, AVI->EventTime, RptNum, IncrementalMinutes));
7064  RetStr += "\nNew service joined by " + AVI->OtherHeadCode + " at " + EventTime;
7065  Utilities->CallLogPop(2235);
7066  return(RetStr);
7067  }
7068  if((AVI->FormatType == TimeLoc) && (AVI->DepartureTime > TDateTime(-1))) //departure time set
7069  {
7070  DepTime = Utilities->Format96HHMM(TrainController->GetControllerTrainTime(17, AVI->DepartureTime, RptNum, IncrementalMinutes));
7071  if(CDTFlag)
7072  {
7073  RetStr += "\nNew service changes direction then departs at " + DepTime;
7074  }
7075  else
7076  {
7077  RetStr += "\nNew service departs at " + DepTime;
7078  }
7079  Utilities->CallLogPop(2236);
7080  return(RetStr);
7081  }
7082  }
7083  Utilities->CallLogPop(2208);
7084  return(RetStr); //if reach here then RetStr doesn't change
7085 }
7086 
7087 // ---------------------------------------------------------------------------
7088 
7090 // Enter with Ptr pointing to first action to be listed (i.e. next action)
7091 // If there are actions to be skipped but a departure is awaited (SkippedDeparture = true) then after the departure Ptr moves forward by SkipPtrValue
7092 {
7093  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + "," + AnsiString(Ptr - &TrainDataEntryPtr->ActionVector.front()) +
7094  ",FloatingTimetableString" + "," + HeadCode);
7095  AnsiString RetStr = "", PartStr = "";
7096  int Count = 0;
7097  bool SkipDep = false, SkipDepActedOn = false; //SkipDepActedOn ensures only one SkipDep acted on
7098  AnsiString LocName = Ptr->LocationName;
7099 
7100  if((Ptr->Command != "") && (Ptr->Command[1] == 'S') && (TrainMode == Timetable))
7101  // can start in signaller control so exclude this
7102  {
7103  throw Exception("Error - start entry in FloatingTimetableString");
7104  }
7105  TActionVectorEntry *EntryPtr = Ptr; //used in TimeTimeLoc check later
7106  bool FirstPass = true;
7107  Ptr--; // because incremented at start of loop
7108 
7109  // different first TimeTimeLoc display if in signaller control
7110  do
7111  {
7112  Ptr++;
7113  if((Ptr->FormatType == Repeat) || TimetableFinished)
7114  {
7115  break;
7116  }
7117  if((Ptr->FormatType == TimeTimeLoc) && FirstPass)
7118  {
7119  AnsiString TrainLoc = "";
7120  if(TrainMode == Timetable)
7121  {
7122  if(TrainAtLocation(1, TrainLoc) && (TrainLoc == Ptr->LocationName) && (Ptr == EntryPtr)) //added '&& (Ptr == EntryPtr)' at v2.6.0 when allow multiple same location entries
7123  {
7124  PartStr = Utilities->Format96HHMM(GetTrainTime(33, Ptr->DepartureTime)) + ": Depart from " + Ptr->LocationName;
7125  if((LocName == Ptr->LocationName) && (LocName != "") && SkippedDeparture && !SkipDepActedOn)
7126  {
7127  SkipDep = true; //0 for incremental minutes because don't reduce the departure time when later actions have been skipped
7128  }
7129  }
7130  else if(Ptr->ArrivalTime == Ptr->DepartureTime)
7131  {
7132  PartStr = Utilities->Format96HHMM(GetTrainTime(34, Ptr->ArrivalTime)) + ": Arrive & depart from " + Ptr->LocationName;
7133  }
7134  else
7135  {
7136  PartStr = Utilities->Format96HHMM(GetTrainTime(16, Ptr->ArrivalTime)) + ": Arrive at " + Ptr->LocationName + '\n' +
7137  Utilities->Format96HHMM(GetTrainTime(17, Ptr->DepartureTime)) + ": Depart from " + Ptr->LocationName;
7138  Count++; // because there are 2 entries
7139  }
7140  }
7141  else // TrainMode == Signaller
7142  {
7143  if(DepartureTimeSet)
7144  {
7145  PartStr = Utilities->Format96HHMM(GetTrainTime(37, Ptr->DepartureTime)) + ": Depart from " + Ptr->LocationName;
7146  if((LocName == Ptr->LocationName) && (LocName != "") && SkippedDeparture && !SkipDepActedOn)
7147  {
7148  SkipDep = true; //0 for incremental minutes because don't reduce the departure time when later actions have been skipped
7149  }
7150  }
7151  else if(Ptr->ArrivalTime == Ptr->DepartureTime)
7152  {
7153  PartStr = Utilities->Format96HHMM(GetTrainTime(38, Ptr->ArrivalTime)) + ": Arrive & depart from " + Ptr->LocationName;
7154  }
7155  else
7156  {
7157  PartStr = Utilities->Format96HHMM(GetTrainTime(39, Ptr->ArrivalTime)) + ": Arrive at " + Ptr->LocationName + '\n' +
7158  Utilities->Format96HHMM(GetTrainTime(40, Ptr->DepartureTime)) + ": Depart from " + Ptr->LocationName;
7159  Count++; // because there are 2 entries
7160  }
7161  }
7162  }
7163  else if((Ptr->FormatType == TimeTimeLoc) && !FirstPass)
7164  {
7165  AnsiString TrainLoc = "";
7166  if((TrainAtLocation(2, TrainLoc)) && (TrainLoc == Ptr->LocationName) && (Ptr == EntryPtr)) //added '&& (Ptr == EntryPtr)' at v2.6.0 when allow multiple same location entries
7167  {
7168  PartStr = Utilities->Format96HHMM(GetTrainTime(41, Ptr->DepartureTime)) + ": Depart from " + Ptr->LocationName;
7169  if((LocName == Ptr->LocationName) && (LocName != "") && SkippedDeparture && !SkipDepActedOn)
7170  {
7171  SkipDep = true; //0 for incremental minutes because don't reduce the departure time when later actions have been skipped
7172  }
7173  }
7174  else if(Ptr->ArrivalTime == Ptr->DepartureTime)
7175  {
7176  PartStr = Utilities->Format96HHMM(GetTrainTime(42, Ptr->ArrivalTime)) + ": Arrive & depart from " + Ptr->LocationName;
7177  }
7178  else
7179  {
7180  PartStr = Utilities->Format96HHMM(GetTrainTime(43, Ptr->ArrivalTime)) + ": Arrive at " + Ptr->LocationName + '\n' +
7181  Utilities->Format96HHMM(GetTrainTime(44, Ptr->DepartureTime)) + ": Depart from " + Ptr->LocationName;
7182  Count++; // because there are 2 entries
7183  }
7184  }
7185  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime != TDateTime(-1)))
7186  {
7187  PartStr = Utilities->Format96HHMM(GetTrainTime(18, Ptr->ArrivalTime)) + ": Arrive at " + Ptr->LocationName;
7188  }
7189  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime == TDateTime(-1)))
7190  {
7191  PartStr = Utilities->Format96HHMM(GetTrainTime(19, Ptr->DepartureTime)) + ": Depart from " + Ptr->LocationName;
7192  if((LocName == Ptr->LocationName) && (LocName != "") && SkippedDeparture && !SkipDepActedOn)
7193  {
7194  SkipDep = true; //0 for incremental minutes because don't reduce the departure time when later actions have been skipped
7195  }
7196  }
7197  else if(Ptr->FormatType == PassTime) // new
7198  {
7199  PartStr = Utilities->Format96HHMM(GetTrainTime(30, Ptr->EventTime)) + ": Pass " + Ptr->LocationName;
7200  }
7201  else if(Ptr->Command == "Fns")
7202  {
7203  PartStr = Utilities->Format96HHMM(GetTrainTime(20, Ptr->EventTime)) + ": Form new service " + TrainController->GetRepeatHeadCode(15,
7204  Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName;
7205  PartStr = CheckNewServiceDepartureTime(5, Ptr, RepeatNumber, Ptr->LinkedTrainEntryPtr, PartStr); //if there is a next service this adds the new service departure time to PartStr
7206  }
7207  else if(Ptr->Command == "F-nshs")
7208  {
7209  PartStr = Utilities->Format96HHMM(GetTrainTime(35, Ptr->EventTime)) + ": Form new service " + Ptr->NonRepeatingShuttleLinkHeadCode + " at " +
7210  Ptr->LocationName;
7211  PartStr = CheckNewServiceDepartureTime(6, Ptr, 0, Ptr->LinkedTrainEntryPtr, PartStr); //if there is a next service this adds the new service departure time to RetStr
7212  //note that use LinkedTrainEntryPtr and not NonRepeatingShuttleLinkEntryPtr because the forward link from the feeder is LinkedTrainEntryPtr.
7213  //NonRepeatingShuttleLinkEntryPtr is in the shuttle's ActionVector to point back to the feeder.
7214  //NonRepeatingShuttleLinkEntryPtr is used below from the last shuttle as the forward link to the finishing service
7215  }
7216  else if((Ptr->Command == "Fns-sh") && (RepeatNumber < (TrainDataEntryPtr->NumberOfTrains - 1))) // not the last repeat number
7217  {
7218  PartStr = Utilities->Format96HHMM(GetTrainTime(21, Ptr->EventTime)) + ": Form new service " + TrainController->GetRepeatHeadCode(16,
7219  Ptr->OtherHeadCode, RepeatNumber + 1, IncrementalDigits) + " at " + Ptr->LocationName;
7220  // use RepeatNumber+1 because it's the repeat number of the NEXT shuttle service that is relevant
7221  PartStr = CheckNewServiceDepartureTime(7, Ptr, RepeatNumber + 1, Ptr->LinkedTrainEntryPtr, PartStr); //if there is a next service this adds the new service departure time to RetStr
7222  }
7223  else if((Ptr->Command == "Fns-sh") && (RepeatNumber >= (TrainDataEntryPtr->NumberOfTrains - 1))) // last repeat number
7224  {
7225  PartStr = Utilities->Format96HHMM(GetTrainTime(22, Ptr->EventTime)) + ": Form new service " + Ptr->NonRepeatingShuttleLinkHeadCode,
7226  +" at " + Ptr->LocationName;
7227  PartStr = CheckNewServiceDepartureTime(8, Ptr, 0, Ptr->NonRepeatingShuttleLinkEntryPtr, PartStr); //if there is a next service this adds the new service departure time to RetStr
7228  }
7229  else if((Ptr->Command == "Frh-sh") && (RepeatNumber < (TrainDataEntryPtr->NumberOfTrains - 1))) // not the last repeat number
7230  {
7231  PartStr = Utilities->Format96HHMM(GetTrainTime(23, Ptr->EventTime)) + ": Form new service " + TrainController->GetRepeatHeadCode(17,
7232  Ptr->OtherHeadCode, RepeatNumber + 1, IncrementalDigits) + " at " + Ptr->LocationName;
7233  // use RepeatNumber+1 because it's the repeat number of the NEXT shuttle service that is relevant
7234  PartStr = CheckNewServiceDepartureTime(9, Ptr, RepeatNumber + 1, Ptr->LinkedTrainEntryPtr, PartStr); //if there is a next service this adds the new service departure time to RetStr
7235  }
7236  else if((Ptr->Command == "Frh-sh") && (RepeatNumber >= (TrainDataEntryPtr->NumberOfTrains - 1))) // last repeat number
7237  {
7238  PartStr = "Terminate at " + Ptr->LocationName;
7239  }
7240  else if(Ptr->Command == "Frh")
7241  {
7242  PartStr = "Terminate at " + Ptr->LocationName;
7243  }
7244  else if(Ptr->Command == "Fer")
7245  {
7246  AnsiString AllowedExits = "";
7247  PartStr = Utilities->Format96HHMM(GetTrainTime(24, Ptr->EventTime)) + ": Exit railway" + TrainController->GetExitLocationAndAt(2, Ptr->ExitList, AllowedExits) + AllowedExits;
7248  }
7249  else if(Ptr->Command == "Fjo")
7250  {
7251  PartStr = Utilities->Format96HHMM(GetTrainTime(25, Ptr->EventTime)) + ": Join " + TrainController->GetRepeatHeadCode(18, Ptr->OtherHeadCode,
7252  RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName;
7253  }
7254  else if(Ptr->Command == "jbo")
7255  {
7256  PartStr = Utilities->Format96HHMM(GetTrainTime(26, Ptr->EventTime)) + ": Joined by " + TrainController->GetRepeatHeadCode(19, Ptr->OtherHeadCode,
7257  RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName;
7258  }
7259  else if(Ptr->Command == "fsp")
7260  {
7261  PartStr = Utilities->Format96HHMM(GetTrainTime(27, Ptr->EventTime)) + ": Front split to " + TrainController->GetRepeatHeadCode(20,
7262  Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName;
7263  }
7264  else if(Ptr->Command == "rsp")
7265  {
7266  PartStr = Utilities->Format96HHMM(GetTrainTime(28, Ptr->EventTime)) + ": Rear split to " + TrainController->GetRepeatHeadCode(21,
7267  Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName;
7268  }
7269  else if(Ptr->Command == "cdt")
7270  {
7271  PartStr = Utilities->Format96HHMM(GetTrainTime(29, Ptr->EventTime)) + ": Change direction at " + Ptr->LocationName;
7272  }
7273  if(RetStr != "")
7274  {
7275  RetStr = RetStr + '\n' + PartStr;
7276  }
7277  else
7278  {
7279  RetStr = PartStr;
7280  }
7281  FirstPass = false;
7282  Count++;
7283 
7284  if(SkipDep)
7285  {
7286  Ptr = &(TrainDataEntryPtr->ActionVector.at(0)) + SkipPtrValue;
7287  Ptr--; //it is incremented at the start of the next loop
7288  SkipDep = false;
7289  SkipDepActedOn = true;
7290  }
7291  }
7292  while(!TimetableFinished && (Count < 32) && ((Ptr->Command == "") || ((Ptr->Command != "") && (Ptr->Command[1] != 'F'))));
7293  // limit of 32 allows a max of 34 entries (33 + 1 for the new service departure time) (may have gone from 32 to 34 because of a TimeTimeLoc), which with track and
7294  // train status gives a max of 48 lines, at 13 pixels each, = 624 pixels & screen height has 641 so will fit comfortably. Also 34 timetable entries is as far
7295  // forward as anyone should wish to see without looking at the full timetable
7296  if(TimetableFinished)
7297  {
7298  if(TrainMode == Timetable)
7299  {
7300  RetStr = "Timetable finished";
7301  }
7302  else
7303  {
7304  RetStr = "No timetable";
7305  }
7306  }
7307  Utilities->CallLogPop(1125);
7308  return(RetStr);
7309 }
7310 
7311 // ---------------------------------------------------------------------------
7312 
7313 void TTrain::SaveOneSessionTrain(int Caller, std::ofstream &OutFile)
7314 {
7315  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SaveOneSessionTrain" + "," + HeadCode);
7316  Utilities->SaveFileString(OutFile, HeadCode);
7319  Utilities->SaveFileInt(OutFile, StartSpeed);
7322  Utilities->SaveFileInt(OutFile, RepeatNumber);
7325  Utilities->SaveFileInt(OutFile, Mass);
7328  Utilities->SaveFileDouble(OutFile, EntrySpeed);
7335  Utilities->SaveFileDouble(OutFile, BrakeRate);
7339  Utilities->SaveFileDouble(OutFile, double(EntryTime));
7340  Utilities->SaveFileDouble(OutFile, double(ExitTimeHalf));
7341  Utilities->SaveFileDouble(OutFile, double(ExitTimeFull));
7342  Utilities->SaveFileDouble(OutFile, double(ReleaseTime));
7343  Utilities->SaveFileDouble(OutFile, double(TRSTime));
7344  Utilities->SaveFileDouble(OutFile, double(LastActionTime));
7348  Utilities->SaveFileInt(OutFile, (short)TrainMode);
7353  Utilities->SaveFileBool(OutFile, Derailed);
7355  Utilities->SaveFileBool(OutFile, Crashed);
7362  Utilities->SaveFileBool(OutFile, NotInService);
7363  Utilities->SaveFileBool(OutFile, Plotted);
7364  Utilities->SaveFileBool(OutFile, TrainGone);
7365  Utilities->SaveFileBool(OutFile, SPADFlag);
7367  Utilities->SaveFileInt(OutFile, HOffset[0]);
7368  Utilities->SaveFileInt(OutFile, HOffset[1]);
7369  Utilities->SaveFileInt(OutFile, HOffset[2]);
7370  Utilities->SaveFileInt(OutFile, HOffset[3]);
7371  Utilities->SaveFileInt(OutFile, VOffset[0]);
7372  Utilities->SaveFileInt(OutFile, VOffset[1]);
7373  Utilities->SaveFileInt(OutFile, VOffset[2]);
7374  Utilities->SaveFileInt(OutFile, VOffset[3]);
7375  Utilities->SaveFileInt(OutFile, PlotElement[0]);
7376  Utilities->SaveFileInt(OutFile, PlotElement[1]);
7377  Utilities->SaveFileInt(OutFile, PlotElement[2]);
7378  Utilities->SaveFileInt(OutFile, PlotElement[3]);
7379  Utilities->SaveFileInt(OutFile, PlotEntryPos[0]);
7380  Utilities->SaveFileInt(OutFile, PlotEntryPos[1]);
7381  Utilities->SaveFileInt(OutFile, PlotEntryPos[2]);
7382  Utilities->SaveFileInt(OutFile, PlotEntryPos[3]);
7384  Utilities->SaveFileInt(OutFile, (short)Straddle);
7385  Utilities->SaveFileInt(OutFile, NextTrainID);
7386  Utilities->SaveFileInt(OutFile, TrainID);
7387  Utilities->SaveFileInt(OutFile, LeadElement);
7388  Utilities->SaveFileInt(OutFile, LeadEntryPos);
7389  Utilities->SaveFileInt(OutFile, LeadExitPos);
7390  Utilities->SaveFileInt(OutFile, MidElement);
7391  Utilities->SaveFileInt(OutFile, MidEntryPos);
7392  Utilities->SaveFileInt(OutFile, MidExitPos);
7393  Utilities->SaveFileInt(OutFile, LagElement);
7394  Utilities->SaveFileInt(OutFile, LagEntryPos);
7395  Utilities->SaveFileInt(OutFile, LagExitPos);
7396  int ColourNumber;
7397 
7399  {
7400  ColourNumber = 0;
7401  }
7403  {
7404  ColourNumber = 1;
7405  }
7407  {
7408  ColourNumber = 2;
7409  }
7411  {
7412  ColourNumber = 3;
7413  }
7415  {
7416  ColourNumber = 4;
7417  }
7419  {
7420  ColourNumber = 5;
7421  }
7423  {
7424  ColourNumber = 6;
7425  }
7427  {
7428  ColourNumber = 7;
7429  }
7431  {
7432  ColourNumber = 8;
7433  }
7435  {
7436  ColourNumber = 9;
7437  }
7439  {
7440  ColourNumber = 10;
7441  }
7443  {
7444  ColourNumber = 11;
7445  }
7447  {
7448  ColourNumber = 12;
7449  }
7450  else if(BackgroundColour == clTRSBackground)
7451  {
7452  ColourNumber = 13;
7453  }
7455  {
7456  ColourNumber = 14; // added at v2.4.0
7457  }
7458  Utilities->SaveFileInt(OutFile, ColourNumber);
7459 
7460  // additional data
7461  bool ForwardHeadCode;
7462 
7463  if(HeadCodePosition[3] == HeadCodeGrPtr[3])
7464  {
7465  ForwardHeadCode = true;
7466  }
7467  // can't use 'if(HeadCodePosition[0] == HeadCodeGrPtr[0])' as HeadCodePosition[0] is set to FrontCodePtr
7468  else
7469  {
7470  ForwardHeadCode = false;
7471  }
7472  Utilities->SaveFileBool(OutFile, ForwardHeadCode);
7473 
7474  int TrainDataEntryValue = TrainDataEntryPtr - &(TrainController->TrainDataVector.at(0));
7475 
7476  Utilities->SaveFileInt(OutFile, TrainDataEntryValue);
7477  int ActionVectorEntryValue = ActionVectorEntryPtr - &(TrainDataEntryPtr->ActionVector.at(0));
7478 
7479  Utilities->SaveFileInt(OutFile, ActionVectorEntryValue);
7480  // now the marker comes next which was ****** originally but used for RestoreTimetableLocation as well some time ago (came before the asterisks)
7481  // but at v2.4.0 need to include StoppedWithoutPower, while keeping length of marker at 6, because that is tested in earlier versions
7482  // so use the last asterisk position for this - 0 for false & 1 for true
7483  // note that failed train data is handled in InterfaceUnit.cpp & stored after the performance file
7484  AnsiString Marker;
7485 
7487  {
7488  Marker = "*****1";
7489  }
7490  else
7491  {
7492  Marker = "*****0";
7493  }
7494  if(RestoreTimetableLocation == "")
7495  {
7496  Utilities->SaveFileString(OutFile, Marker);
7497  }
7498  else
7499  {
7500  AnsiString CombinedString = RestoreTimetableLocation + Marker;
7501  Utilities->SaveFileString(OutFile, CombinedString);
7502  // RestoreTimetableLocation + marker
7503  }
7504  // Note: including RestoreTimetableLocation with the marker is to correct an oversight - it should have been saved earlier
7505  Utilities->CallLogPop(1457);
7506 }
7507 
7508 // ---------------------------------------------------------------------------
7509 
7510 void TTrain::LoadOneSessionTrain(int Caller, std::ifstream &InFile)
7511 {
7512  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",LoadOneSessionTrain"); // don't have headcode yet
7513  HeadCode = Utilities->LoadFileString(InFile);
7516  StartSpeed = Utilities->LoadFileInt(InFile);
7518  if(SignallerMaxSpeed < 10)
7519  {
7520  SignallerMaxSpeed = 10; // added at v0.6 to avoid low max speeds
7521  }
7523  RepeatNumber = Utilities->LoadFileInt(InFile);
7526  Mass = Utilities->LoadFileInt(InFile);
7529  {
7531  }
7532  // above added at v2.1.0 for legacy session files where value may not have been limited
7534  EntrySpeed = Utilities->LoadFileDouble(InFile);
7538  if(TimetableMaxRunningSpeed < 10)
7539  {
7540  TimetableMaxRunningSpeed = 10; // added at v0.6 to avoid low max speeds
7541  }
7543  if(MaxRunningSpeed < 10)
7544  {
7545  MaxRunningSpeed = 10; // added at v0.6 to avoid low max speeds
7546  }
7549  BrakeRate = Utilities->LoadFileDouble(InFile);
7553  EntryTime = TDateTime(Utilities->LoadFileDouble(InFile));
7554  ExitTimeHalf = TDateTime(Utilities->LoadFileDouble(InFile));
7555  ExitTimeFull = TDateTime(Utilities->LoadFileDouble(InFile));
7556  ReleaseTime = TDateTime(Utilities->LoadFileDouble(InFile));
7557  TRSTime = TDateTime(Utilities->LoadFileDouble(InFile));
7558  LastActionTime = TDateTime(Utilities->LoadFileDouble(InFile));
7567  Derailed = Utilities->LoadFileBool(InFile);
7569  Crashed = Utilities->LoadFileBool(InFile);
7576  NotInService = Utilities->LoadFileBool(InFile);
7577  Plotted = Utilities->LoadFileBool(InFile);
7578  TrainGone = Utilities->LoadFileBool(InFile);
7579  SPADFlag = Utilities->LoadFileBool(InFile);
7581  HOffset[0] = Utilities->LoadFileInt(InFile);
7582  HOffset[1] = Utilities->LoadFileInt(InFile);
7583  HOffset[2] = Utilities->LoadFileInt(InFile);
7584  HOffset[3] = Utilities->LoadFileInt(InFile);
7585  VOffset[0] = Utilities->LoadFileInt(InFile);
7586  VOffset[1] = Utilities->LoadFileInt(InFile);
7587  VOffset[2] = Utilities->LoadFileInt(InFile);
7588  VOffset[3] = Utilities->LoadFileInt(InFile);
7589  PlotElement[0] = Utilities->LoadFileInt(InFile);
7590  PlotElement[1] = Utilities->LoadFileInt(InFile);
7591  PlotElement[2] = Utilities->LoadFileInt(InFile);
7592  PlotElement[3] = Utilities->LoadFileInt(InFile);
7593  PlotEntryPos[0] = Utilities->LoadFileInt(InFile);
7594  PlotEntryPos[1] = Utilities->LoadFileInt(InFile);
7595  PlotEntryPos[2] = Utilities->LoadFileInt(InFile);
7596  PlotEntryPos[3] = Utilities->LoadFileInt(InFile);
7598  Straddle = (TStraddle)(Utilities->LoadFileInt(InFile));
7599  NextTrainID = Utilities->LoadFileInt(InFile);
7600  // will be same for all but best to save all anyway
7601  TrainID = Utilities->LoadFileInt(InFile);
7602  LeadElement = Utilities->LoadFileInt(InFile);
7603  LeadEntryPos = Utilities->LoadFileInt(InFile);
7604  LeadExitPos = Utilities->LoadFileInt(InFile);
7605  MidElement = Utilities->LoadFileInt(InFile);
7606  MidEntryPos = Utilities->LoadFileInt(InFile);
7607  MidExitPos = Utilities->LoadFileInt(InFile);
7608  LagElement = Utilities->LoadFileInt(InFile);
7609  LagEntryPos = Utilities->LoadFileInt(InFile);
7610  LagExitPos = Utilities->LoadFileInt(InFile);
7611  int ColourNumber = TColor(Utilities->LoadFileInt(InFile));
7612 
7613  if(ColourNumber == 0)
7614  {
7616  }
7617  else if(ColourNumber == 1)
7618  {
7620  }
7621  else if(ColourNumber == 2)
7622  {
7624  }
7625  else if(ColourNumber == 3)
7626  {
7628  }
7629  else if(ColourNumber == 4)
7630  {
7632  }
7633  else if(ColourNumber == 5)
7634  {
7636  }
7637  else if(ColourNumber == 6)
7638  {
7640  }
7641  else if(ColourNumber == 7)
7642  {
7644  }
7645  else if(ColourNumber == 8)
7646  {
7648  }
7649  else if(ColourNumber == 9)
7650  {
7652  }
7653  else if(ColourNumber == 10)
7654  {
7656  }
7657  else if(ColourNumber == 11)
7658  {
7660  }
7661  else if(ColourNumber == 12)
7662  {
7664  }
7665  else if(ColourNumber == 13)
7666  {
7668  }
7669  else if(ColourNumber == 14)
7670  {
7671  BackgroundColour = clTrainFailedBackground; // added at v2.4.0
7672 
7673  }
7674  // additional data
7676  // sets the BackgroundColour to the loaded value
7677  bool ForwardHeadCode = Utilities->LoadFileBool(InFile);
7678 
7679  if(ForwardHeadCode)
7680  {
7681  for(int x = 0; x < 4; x++)
7682  {
7684  }
7685  }
7686  else
7687  {
7688  for(int x = 0; x < 4; x++)
7689  {
7690  HeadCodePosition[x] = HeadCodeGrPtr[3 - x];
7691  }
7692  }
7693  // if crashed & in timetable mode then change FrontCodePtr to black, if in signaller mode then change to blue whether crashed or not
7694  if(TrainMode == Timetable)
7695  {
7696  if(Crashed)
7697  {
7699  }
7700  else
7701  {
7703  }
7704  }
7705  else
7706  {
7708  }
7710  // pick up background bitmaps, none if MidLag as no train plotted - entering at continuation
7711  if(Straddle == LeadMid)
7712  {
7713  if(LeadElement > -1)
7714  {
7716  }
7717  if(LeadElement > -1)
7718  {
7720  }
7721  if(MidElement > -1)
7722  {
7724  }
7725  if(MidElement > -1)
7726  {
7728  }
7729  }
7730  else if(Straddle == LeadMidLag)
7731  {
7732  if(LeadElement > -1)
7733  {
7735  }
7736  if(MidElement > -1)
7737  {
7739  }
7740  if(MidElement > -1)
7741  {
7743  }
7744  if(LagElement > -1)
7745  {
7747  }
7748  }
7749  int TrainDataEntryValue = Utilities->LoadFileInt(InFile);
7750 
7751  TrainDataEntryPtr = &(TrainController->TrainDataVector.at(0)) + TrainDataEntryValue;
7752  int ActionVectorEntryValue = Utilities->LoadFileInt(InFile);
7753 
7754  ActionVectorEntryPtr = &(TrainDataEntryPtr->ActionVector.at(0)) + ActionVectorEntryValue;
7755 
7756  // need to set the TrainID if arriving at a continuation but hasn't been plotted yet
7757  if(LeadElement > -1)
7758  // need to include this in case train exiting & no lead element
7759  {
7761  {
7762  Track->TrackElementAt(668, LeadElement).TrainIDOnElement = TrainID; // no need to stop gap flashing if a continuation
7763  }
7764  }
7765  AValue = sqrt(2 * PowerAtRail / Mass);
7766 
7767  AnsiString LocationAndMarker = Utilities->LoadFileString(InFile);
7768 
7769  // possible RestoreTimetableLocation + Marker, where Marker is
7770  // "*****0" for !StoppedWithoutPower and "*****1" otherwise (from v2.4.0)
7771  // Note: including RestoreTimetableLocation with the marker is to correct an oversight - RestoreTimetableLocation should have been saved earlier
7772  // added at beta v0.2e
7773  if((LocationAndMarker[1] != '*') && (LocationAndMarker.Length() > 6))
7774  // name not allowed to include the '*' character
7775  {
7776  AnsiString Location = LocationAndMarker.SubString(1, LocationAndMarker.Length() - 6);
7777  bool GiveMessagesFalse = false;
7778  bool CheckLocationsExistInRailwayTrue = true;
7779  if(TrainController->CheckLocationValidity(3, Location, GiveMessagesFalse, CheckLocationsExistInRailwayTrue))
7780  {
7781  // otherwise take no action
7782  RestoreTimetableLocation = Location;
7783  }
7784  }
7785  AnsiString Marker = LocationAndMarker.SubString(LocationAndMarker.Length() - 5, 6);
7786 
7787  StoppedWithoutPower = false;
7788  if(Marker[6] == '1')
7789  {
7790  StoppedWithoutPower = true;
7791  }
7792  Utilities->CallLogPop(1458);
7793 }
7794 
7795 // ---------------------------------------------------------------------------
7796 
7797 bool TTrain::CheckOneSessionTrain(std::ifstream &InFile)
7798 {
7800  {
7801  return(false); // HeadCode
7802 
7803  }
7804  if(!Utilities->CheckFileInt(InFile, 0, 1000000))
7805  {
7806  return(false); // RearStartElement
7807 
7808  }
7809  if(!Utilities->CheckFileInt(InFile, 0, 3))
7810  {
7811  return(false); // RearStartExitPos
7812 
7813  }
7814  if(!Utilities->CheckFileInt(InFile, 0, MaximumSpeedLimit))
7815  {
7816  return(false); // StartSpeed
7817 
7818  }
7819  if(!Utilities->CheckFileInt(InFile, 0, MaximumSpeedLimit))
7820  {
7821  return(false); // SignallerMaxSpeed
7822 
7823  }
7824  if(!Utilities->CheckFileBool(InFile))
7825  {
7826  return(false); // HoldAtLocationInTTMode
7827 
7828  }
7829  if(!Utilities->CheckFileInt(InFile, 0, 5760))
7830  {
7831  return(false); // RepeatNumber (max 96 x 60 at 1 min intervals)
7832 
7833  }
7834  if(!Utilities->CheckFileInt(InFile, 0, 5760))
7835  {
7836  return(false); // IncrementalMinutes (max 96 x 60)
7837 
7838  }
7839  if(!Utilities->CheckFileInt(InFile, 0, 1000000))
7840  {
7841  return(false); // IncrementalDigits
7842 
7843  }
7844  if(!Utilities->CheckFileInt(InFile, 0, 10000000))
7845  {
7846  return(false); // Mass
7847 
7848  }
7849  if(!Utilities->CheckFileInt(InFile, 0, 100000000))
7850  {
7851  return(false);
7852  }
7853  // FrontElementSpeedLimit - changed at v2.1.0 - effectively
7854  // not checked so as to allow for legacy session files, for new session files limit is set when loaded, see above
7855  if(!Utilities->CheckFileInt(InFile, 0, 10000000))
7856  {
7857  return(false); // FrontElementLength
7858 
7859  }
7860  if(!Utilities->CheckFileDouble(InFile))
7861  {
7862  return(false); // EntrySpeed
7863 
7864  }
7865  if(!Utilities->CheckFileDouble(InFile))
7866  {
7867  return(false); // ExitSpeedHalf
7868 
7869  }
7870  if(!Utilities->CheckFileDouble(InFile))
7871  {
7872  return(false); // ExitSpeedFull
7873 
7874  }
7875  if(!Utilities->CheckFileDouble(InFile))
7876  {
7877  return(false); // TimetableMaxRunningSpeed
7878 
7879  }
7880  if(!Utilities->CheckFileDouble(InFile))
7881  {
7882  return(false); // MaxRunningSpeed
7883 
7884  }
7885  if(!Utilities->CheckFileDouble(InFile))
7886  {
7887  return(false); // MaxExitSpeed
7888 
7889  }
7890  if(!Utilities->CheckFileDouble(InFile))
7891  {
7892  return(false); // MaxBrakeRate
7893 
7894  }
7895  if(!Utilities->CheckFileDouble(InFile))
7896  {
7897  return(false); // BrakeRate
7898 
7899  }
7900  if(!Utilities->CheckFileDouble(InFile))
7901  {
7902  return(false); // PowerAtRail
7903 
7904  }
7905  if(!Utilities->CheckFileBool(InFile))
7906  {
7907  return(false); // FirstHalfMove
7908 
7909  }
7910  if(!Utilities->CheckFileBool(InFile))
7911  {
7912  return(false); // OneLengthAccelDecel
7913 
7914  }
7915  if(!Utilities->CheckFileDouble(InFile))
7916  {
7917  return(false); // double(EntryTime)
7918 
7919  }
7920  if(!Utilities->CheckFileDouble(InFile))
7921  {
7922  return(false); // double(ExitTimeHalf)
7923 
7924  }
7925  if(!Utilities->CheckFileDouble(InFile))
7926  {
7927  return(false); // double(ExitTimeFull)
7928 
7929  }
7930  if(!Utilities->CheckFileDouble(InFile))
7931  {
7932  return(false); // double(ReleaseTime)
7933 
7934  }
7935  if(!Utilities->CheckFileDouble(InFile))
7936  {
7937  return(false); // double(TRSTime)
7938 
7939  }
7940  if(!Utilities->CheckFileDouble(InFile))
7941  {
7942  return(false); // double(LastActionTime)
7943 
7944  }
7945  if(!Utilities->CheckFileBool(InFile))
7946  {
7947  return(false); // CallingOnFlag
7948 
7949  }
7950  if(!Utilities->CheckFileBool(InFile))
7951  {
7952  return(false); // BeingCalledOn
7953 
7954  }
7955  if(!Utilities->CheckFileBool(InFile))
7956  {
7957  return(false); // DepartureTimeSet
7958 
7959  }
7960  if(!Utilities->CheckFileInt(InFile, 0, 2))
7961  {
7962  return(false); // (short)TrainMode
7963 
7964  }
7965  if(!Utilities->CheckFileBool(InFile))
7966  {
7967  return(false); // TimetableFinished
7968 
7969  }
7970  if(!Utilities->CheckFileBool(InFile))
7971  {
7972  return(false); // LastActionDelayFlag
7973 
7974  }
7975  if(!Utilities->CheckFileBool(InFile))
7976  {
7977  return(false); // SignallerRemoved
7978 
7979  }
7980  if(!Utilities->CheckFileBool(InFile))
7981  {
7982  return(false); // TerminatedMessageSent
7983 
7984  }
7985  if(!Utilities->CheckFileBool(InFile))
7986  {
7987  return(false); // Derailed
7988 
7989  }
7990  if(!Utilities->CheckFileBool(InFile))
7991  {
7992  return(false); // DerailPending
7993 
7994  }
7995  if(!Utilities->CheckFileBool(InFile))
7996  {
7997  return(false); // Crashed
7998 
7999  }
8000  if(!Utilities->CheckFileBool(InFile))
8001  {
8002  return(false); // StoppedAtBuffers
8003 
8004  }
8005  if(!Utilities->CheckFileBool(InFile))
8006  {
8007  return(false); // StoppedAtSignal
8008 
8009  }
8010  if(!Utilities->CheckFileBool(InFile))
8011  {
8012  return(false); // StoppedAtLocation
8013 
8014  }
8015  if(!Utilities->CheckFileBool(InFile))
8016  {
8017  return(false); // SignallerStopped
8018 
8019  }
8020  if(!Utilities->CheckFileBool(InFile))
8021  {
8022  return(false); // StoppedAfterSPAD
8023 
8024  }
8025  if(!Utilities->CheckFileBool(InFile))
8026  {
8027  return(false); // StoppedForTrainInFront
8028 
8029  }
8030  if(!Utilities->CheckFileBool(InFile))
8031  {
8032  return(false); // NotInService
8033 
8034  }
8035  if(!Utilities->CheckFileBool(InFile))
8036  {
8037  return(false); // Plotted
8038 
8039  }
8040  if(!Utilities->CheckFileBool(InFile))
8041  {
8042  return(false); // TrainGone
8043 
8044  }
8045  if(!Utilities->CheckFileBool(InFile))
8046  {
8047  return(false); // SPADFlag
8048 
8049  }
8050  if(!Utilities->CheckFileBool(InFile))
8051  {
8052  return(false); // TimeTimeLocArrived
8053 
8054  }
8055  if(!Utilities->CheckFileInt(InFile, 0, 15))
8056  {
8057  return(false); // HOffset[0]
8058 
8059  }
8060  if(!Utilities->CheckFileInt(InFile, 0, 15))
8061  {
8062  return(false); // HOffset[1]
8063 
8064  }
8065  if(!Utilities->CheckFileInt(InFile, 0, 15))
8066  {
8067  return(false); // HOffset[2]
8068 
8069  }
8070  if(!Utilities->CheckFileInt(InFile, 0, 15))
8071  {
8072  return(false); // HOffset[3]
8073 
8074  }
8075  if(!Utilities->CheckFileInt(InFile, 0, 15))
8076  {
8077  return(false); // VOffset[0]
8078 
8079  }
8080  if(!Utilities->CheckFileInt(InFile, 0, 15))
8081  {
8082  return(false); // VOffset[1]
8083 
8084  }
8085  if(!Utilities->CheckFileInt(InFile, 0, 15))
8086  {
8087  return(false); // VOffset[2]
8088 
8089  }
8090  if(!Utilities->CheckFileInt(InFile, 0, 15))
8091  {
8092  return(false); // VOffset[3]
8093 
8094  }
8095  if(!Utilities->CheckFileInt(InFile, -1, 1000000))
8096  {
8097  return(false); // PlotElement[0]
8098 
8099  }
8100  if(!Utilities->CheckFileInt(InFile, -1, 1000000))
8101  {
8102  return(false); // PlotElement[1]
8103 
8104  }
8105  if(!Utilities->CheckFileInt(InFile, -1, 1000000))
8106  {
8107  return(false); // PlotElement[2]
8108 
8109  }
8110  if(!Utilities->CheckFileInt(InFile, -1, 1000000))
8111  {
8112  return(false); // PlotElement[3]
8113 
8114  }
8115  if(!Utilities->CheckFileInt(InFile, 0, 3))
8116  {
8117  return(false); // PlotEntryPos[0]
8118 
8119  }
8120  if(!Utilities->CheckFileInt(InFile, 0, 3))
8121  {
8122  return(false); // PlotEntryPos[1]
8123 
8124  }
8125  if(!Utilities->CheckFileInt(InFile, 0, 3))
8126  {
8127  return(false); // PlotEntryPos[2]
8128 
8129  }
8130  if(!Utilities->CheckFileInt(InFile, 0, 3))
8131  {
8132  return(false); // PlotEntryPos[3]
8133 
8134  }
8135  if(!Utilities->CheckFileInt(InFile, -1, 1000000))
8136  {
8137  return(false); // TrainCrashedInto
8138 
8139  }
8140  if(!Utilities->CheckFileInt(InFile, 0, 2))
8141  {
8142  return(false); // (short)Straddle
8143 
8144  }
8145  if(!Utilities->CheckFileInt(InFile, 0, 1000000))
8146  {
8147  return(false); // NextTrainID
8148 
8149  }
8150  if(!Utilities->CheckFileInt(InFile, 0, 1000000))
8151  {
8152  return(false); // TrainID
8153 
8154  }
8155  if(!Utilities->CheckFileInt(InFile, -1, 1000000))
8156  {
8157  return(false); // LeadElement
8158 
8159  }
8160  if(!Utilities->CheckFileInt(InFile, 0, 3))
8161  {
8162  return(false); // LeadEntryPos
8163 
8164  }
8165  if(!Utilities->CheckFileInt(InFile, 0, 3))
8166  {
8167  return(false); // LeadExitPos
8168 
8169  }
8170  if(!Utilities->CheckFileInt(InFile, -1, 1000000))
8171  {
8172  return(false); // MidElement
8173 
8174  }
8175  if(!Utilities->CheckFileInt(InFile, 0, 3))
8176  {
8177  return(false); // MidEntryPos
8178 
8179  }
8180  if(!Utilities->CheckFileInt(InFile, 0, 3))
8181  {
8182  return(false); // MidExitPos
8183 
8184  }
8185  if(!Utilities->CheckFileInt(InFile, -1, 1000000))
8186  {
8187  return(false); // LagElement
8188 
8189  }
8190  if(!Utilities->CheckFileInt(InFile, 0, 3))
8191  {
8192  return(false); // LagEntryPos
8193 
8194  }
8195  if(!Utilities->CheckFileInt(InFile, 0, 3))
8196  {
8197  return(false); // LagExitPos
8198 
8199  }
8200  if(!Utilities->CheckFileInt(InFile, 0, 14))
8201  {
8202  return(false);
8203  }
8204  // Background colour number //14 is failed colour at v2.4.0
8205  if(!Utilities->CheckFileBool(InFile))
8206  {
8207  return(false); // ForwardHeadCode
8208 
8209  }
8210  if(!Utilities->CheckFileInt(InFile, 0, 10000))
8211  {
8212  return(false); // TrainDataEntryValue
8213 
8214  }
8215  if(!Utilities->CheckFileInt(InFile, 0, 10000))
8216  {
8217  return(false); // ActionVectorEntryValue
8218 
8219  }
8221  {
8222  return(false); // End of train marker + possible RestoreTimetableLocation
8223 
8224  }
8225  // and StoppedWithoutPower flag
8226  return(true);
8227 }
8228 
8229 // ---------------------------------------------------------------------------
8230 
8231 void TTrain::PlotTrainInZoomOutMode(int Caller, bool Flash)
8232 {
8233  // order below reflects significance so earlier shows first, as may have more than one flag set
8234  // only plot flashing trains when Flash is true
8235 
8236 /*
8237  clCrashedBackground (TColor)0x0000FF red
8238  clDerailedBackground (TColor)0x0000FF red
8239  clSPADBackground (TColor)0x00FFFF yellow
8240  clTrainFailedBackground (TColor)0x0066FF orange new at v2.4.0
8241  clCallOnBackground (TColor)0xFF33FF light magenta
8242  clSignalStopBackground (TColor)0x00FF66 green
8243  clBufferAttentionNeeded (TColor)0xFFFF00 cyan
8244  clStationStopBackground (TColor)0xCCFFCC pale green
8245  clTRSBackground (TColor)0xFFCCFF light pink
8246  clBufferStopBackground (TColor)0xFFFFCC pale cyan
8247  clStoppedTrainInFront (TColor)0xFF9999 lavender blue
8248  clSignallerStopped (TColor)0x99CCFF caramel
8249  clNormalBackground (TColor)0xCCCCCC grey
8250 */
8251 
8252  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PlotTrainInZoomOutMode" + "," + HeadCode);
8253  bool HideFlashingTrain = true;
8254  // hide it when Flash false so it blinks on and off
8255  // if don't hide it it stays displayed all the time
8256  Graphics::TBitmap *SmallTrainBitmap;
8257 
8258  // NB ensure retain same order as zoomed in order so colours correspond
8260  {
8261  TrainController->CrashWarning = true;
8262  SmallTrainBitmap = RailGraphics->smRed;
8263  }
8265  {
8267  SmallTrainBitmap = RailGraphics->smRed;
8268  }
8270  {
8271  TrainController->SPADWarning = true;
8272  SmallTrainBitmap = RailGraphics->smYellow;
8273  }
8275  {
8277  SmallTrainBitmap = RailGraphics->smOrange;
8278  }
8280  {
8282  SmallTrainBitmap = RailGraphics->smMagenta;
8283  }
8285  {
8287  SmallTrainBitmap = RailGraphics->smBrightGreen;
8288  }
8290  {
8292  SmallTrainBitmap = RailGraphics->smCyan;
8293  }
8295  {
8296  SmallTrainBitmap = RailGraphics->smPaleGreen;
8297  HideFlashingTrain = false;
8298  }
8300  {
8301  SmallTrainBitmap = RailGraphics->smCyan;
8302  HideFlashingTrain = false;
8303  }
8305  {
8306  SmallTrainBitmap = RailGraphics->smLightBlue;
8307  HideFlashingTrain = false;
8308  }
8310  {
8311  SmallTrainBitmap = RailGraphics->smCaramel;
8312  HideFlashingTrain = false;
8313  }
8314  else
8315  {
8316  SmallTrainBitmap = RailGraphics->smBlack; // moving
8317  HideFlashingTrain = false;
8318  }
8319  // now plot the new train
8320  // just plot lead & mid, unless lead == -1 in which case plot mid & lag
8321  if((LeadElement > -1) && (!HideFlashingTrain || Flash))
8322  {
8323  Display->PlotSmallOutput(4, Track->TrackElementAt(441, LeadElement).HLoc * 4, Track->TrackElementAt(442, LeadElement).VLoc * 4, SmallTrainBitmap);
8324  }
8325  if((MidElement > -1) && (!HideFlashingTrain || Flash))
8326  {
8327  Display->PlotSmallOutput(5, Track->TrackElementAt(443, MidElement).HLoc * 4, Track->TrackElementAt(444, MidElement).VLoc * 4, SmallTrainBitmap);
8328  }
8329  if((LeadElement == -1) && (LagElement > -1) && (!HideFlashingTrain || Flash))
8330  {
8331  Display->PlotSmallOutput(6, Track->TrackElementAt(445, LagElement).HLoc * 4, Track->TrackElementAt(446, LagElement).VLoc * 4, SmallTrainBitmap);
8332  }
8336  Utilities->CallLogPop(1459);
8337 }
8338 
8339 // ---------------------------------------------------------------------------
8340 
8342 {
8343  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",UnplotTrainInZoomOutMode," + AnsiString(TrainID) + "," + HeadCode);
8344  if(!Display->ZoomOutFlag)
8345  {
8346  Utilities->CallLogPop(1304);
8347  return;
8348  }
8349  for(int y = 0; y < 3; y++)
8350  {
8351  if(OldZoomOutElement[y] > -1)
8352  {
8353  bool FoundFlag = false;
8354  TTrackElement ATElement = Track->TrackElementAt(717, OldZoomOutElement[y]);
8355  TTrackElement IATElement1, IATElement2;
8356  // default elements to begin with
8357  Display->PlotSmallOutput(7, ATElement.HLoc * 4, ATElement.VLoc * 4, RailGraphics->smSolidBgnd); // plot the blank
8358  TTrack::TIMPair IMPair = Track->GetVectorPositionsFromInactiveTrackMap(14, ATElement.HLoc, ATElement.VLoc, FoundFlag);
8359  // Note, have to plot inactives before track because track has to overwrite NamedLocationElements
8360  if(FoundFlag)
8361  {
8362  IATElement1 = Track->InactiveTrackElementAt(87, IMPair.first);
8363  Display->PlotSmallOutput(8, IATElement1.HLoc * 4, IATElement1.VLoc * 4, IATElement1.SmallGraphicPtr);
8364  if(IMPair.first != IMPair.second)
8365  {
8366  IATElement2 = Track->InactiveTrackElementAt(88, IMPair.second);
8367  Display->PlotSmallOutput(9, IATElement2.HLoc * 4, IATElement2.VLoc * 4, IATElement2.SmallGraphicPtr);
8368  }
8369  }
8370  Display->PlotSmallOutput(10, ATElement.HLoc * 4, ATElement.VLoc * 4, ATElement.SmallGraphicPtr);
8371  }
8372  }
8373  Utilities->CallLogPop(1305);
8374 }
8375 
8376 // ---------------------------------------------------------------------------
8377 
8378 bool TTrain::TrainAtLocation(int Caller, AnsiString &LocationName)
8379 {
8380  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TrainAtLocation" + "," + HeadCode);
8381  LocationName = "";
8382  if(!StoppedAtLocation)
8383  {
8384  Utilities->CallLogPop(1398);
8385  return(false);
8386  }
8387  if(LeadElement > -1)
8388  {
8390  }
8391  if((LocationName == "") && (MidElement > -1))
8392  {
8393  LocationName = Track->TrackElementAt(682, MidElement).ActiveTrackElementName;
8394  }
8395  if((LocationName == "") && (LagElement > -1))
8396  {
8397  LocationName = Track->TrackElementAt(683, LagElement).ActiveTrackElementName;
8398  }
8399  if(LocationName == "")
8400  {
8401  throw Exception("Error - Location name not set in TrainAtLocation");
8402  }
8403  Utilities->CallLogPop(1399);
8404  return(true);
8405 }
8406 
8407 // ---------------------------------------------------------------------------
8408 
8409 void TTrain::PlotTrain(int Caller, TDisplay *Disp)
8410 {
8411  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PlotTrain" + "," + HeadCode);
8412  for(int x = 0; x < 4; x++)
8413  {
8414  PlotTrainGraphic(7, x, Disp);
8415  }
8416  Utilities->CallLogPop(647);
8417 }
8418 
8419 // ---------------------------------------------------------------------------
8420 
8421 void TTrain::WriteTrainToImage(int Caller, Graphics::TBitmap *Bitmap)
8422 {
8423  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",WriteTrainToImage" + "," + HeadCode);
8424  for(int x = 0; x < 4; x++)
8425  {
8426  if(PlotElement[x] > -1)
8427  {
8428  Bitmap->Canvas->Draw(((Track->TrackElementAt(744, PlotElement[x]).HLoc - Track->GetHLocMin()) * 16 + HOffset[x]),
8429  ((Track->TrackElementAt(745, PlotElement[x]).VLoc - Track->GetVLocMin()) * 16 + VOffset[x]), HeadCodePosition[x]);
8430  }
8431  }
8432  Utilities->CallLogPop(1708);
8433 }
8434 
8435 // ---------------------------------------------------------------------------
8436 
8437 bool TTrain::LinkOccupied(int Caller, int TrackVectorPosition, int LinkNumber) // added at v1.2.0
8438 {
8439  // return true for any part of train occupying LinkNumber at TrackVectorPosition, false for anything else, including no LinkNumber & no TrackVectorPosition
8440  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",LinkOccupied," + AnsiString(TrackVectorPosition) + "," +
8441  AnsiString(LinkNumber) + "," + HeadCode);
8442 
8443 /* Note on Straddle: Straddle defines the actual train position wrt Lag, Mid & Lead elements at all times other than within UpdateTrain. Is only MidLag outside UpdateTrain
8444  on first entry at a continuation (with no train plotted), and that has no relevance here. In all other cases it is either LeadMid (when train fully
8445  on Lead & Mid elements) or LeadMidLag (when train straddling 3 elements).
8446 */
8447 
8448  // note that MidElement always fully occupied
8449  if((MidElement == TrackVectorPosition) && ((Track->TrackElementAt(883, TrackVectorPosition).Link[MidEntryPos] == LinkNumber) || (Track->TrackElementAt(884,
8450  TrackVectorPosition).Link[MidExitPos] == LinkNumber)))
8451  {
8452  Utilities->CallLogPop(2005);
8453  return(true);
8454  }
8455  if(Straddle == LeadMid)
8456  {
8457  if((LeadElement == TrackVectorPosition) && ((Track->TrackElementAt(885, TrackVectorPosition).Link[LeadEntryPos] == LinkNumber) ||
8458  (Track->TrackElementAt(886, TrackVectorPosition).Link[LeadExitPos] == LinkNumber)))
8459  {
8460  Utilities->CallLogPop(2006);
8461  return(true);
8462  }
8463  }
8464  else if(Straddle == LeadMidLag)
8465  {
8466  if((LeadElement == TrackVectorPosition) && (Track->TrackElementAt(887, TrackVectorPosition).Link[LeadEntryPos] == LinkNumber))
8467  // only interested in LeadEntryPos as train not occupying ExitPos yet
8468  {
8469  Utilities->CallLogPop(2007);
8470  return(true);
8471  }
8472  else if((LagElement == TrackVectorPosition) && (Track->TrackElementAt(888, TrackVectorPosition).Link[LagExitPos] == LinkNumber))
8473  // only interested in LagExitPos as train has left EntryPos
8474  {
8475  Utilities->CallLogPop(2008);
8476  return(true);
8477  }
8478  }
8479  Utilities->CallLogPop(2009);
8480  return(false);
8481 }
8482 
8483 // ---------------------------------------------------------------------------
8484 
8485 float TTrain::CalcTimeToAct(int Caller, float &TimeToExit, THVShortPair &ExitPair) // only called for running trains.
8490 // TimeToExit & ExitPair added for multiplayer
8491 {
8492  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CalcTimeToAct, " + HeadCode);
8493  int DistanceToRedSignal = 0, DistanceToExit = -1;
8494  float TimeToAct = 0, LastTimeToExit = TimeToExit;
8495  TimeToExit = -1;
8496  ExitPair.first = -1;
8497  ExitPair.second = -1;
8498  float MinsEarly = 0; //added at v2.6.1
8499  TDateTime DepartureTime; //added at v2.6.1 //ArrivalTime used instead of this at v2.9.0 but still calculate it in case need it later for some reason
8500  TDateTime ArrivalTime; //added at v2.9.0 as MinsEarly used DepartureTime which wasn't correct
8501  float TempTTE;
8502 
8503  if(TrainFailed)
8504  {
8505  Utilities->CallLogPop(2147);
8506  return(0); // time to act now, time to exit set above
8507  }
8508  if(SignallerStopped)
8509  {
8510  Utilities->CallLogPop(2080);
8511  return(-1); //time to exit set above
8512  }
8513  if(BeingCalledOn) //added at v2.7.0 so zero time to act cancelled right away
8514  {
8515  Utilities->CallLogPop(2266);
8516  return(-1); // time to exit set above
8517  }
8518 
8519  // check if if exiting at a continuation, if so there's no action time needed but still need exit time & ExitPair
8520  if((LeadElement == -1) && (MidElement == -1) && (LagElement > -1) && (Track->TrackElementAt(1411, LagElement).TrackType == Continuation)
8521  && (ExitSpeedFull > 1)) //LagElement is the exit
8522  {
8523  if(Straddle == LeadMidLag) //only half of rear train element on exit, 0.5 lengths to exit
8524  {
8525  TempTTE = (0.5 * Track->TrackElementAt(1412, LagElement).Length01) * 3.6 / 60 / ExitSpeedFull;
8526  if(TempTTE < LastTimeToExit)
8527  {
8528  TimeToExit = TempTTE; //else leave as is, don't want a sudden increase in time
8529  }
8530  else
8531  {
8532  TimeToExit = LastTimeToExit;
8533  }
8534  ExitPair.first = Track->TrackElementAt(1413, LagElement).HLoc;
8535  ExitPair.second = Track->TrackElementAt(1414, LagElement).VLoc;
8536  Utilities->CallLogPop(2342);
8537  return(-1);
8538  }
8539  else
8540  {
8541  TimeToExit = 0; //all train exited
8542  ExitPair.first = Track->TrackElementAt(1415, LagElement).HLoc;
8543  ExitPair.second = Track->TrackElementAt(1416, LagElement).VLoc;
8544  Utilities->CallLogPop(2343);
8545  return(-1);
8546  }
8547  }
8548  if((LeadElement == -1) && (MidElement > -1) && (Track->TrackElementAt(1417, MidElement).TrackType == Continuation) && (ExitSpeedFull > 1))
8549  //here MidElement is the exit
8550  {
8551  if(Straddle == LeadMidLag) //front element of train half off the exit, 1.5 lengths to exit
8552  {
8553  TempTTE = (1.5 * Track->TrackElementAt(1418, MidElement).Length01) * 3.6 / 60 / ExitSpeedFull;
8554  if(TempTTE < LastTimeToExit)
8555  {
8556  TimeToExit = TempTTE; //else leave as is, don't want a sudden increase in time
8557  }
8558  else
8559  {
8560  TimeToExit = LastTimeToExit;
8561  }
8562  ExitPair.first = Track->TrackElementAt(1419, MidElement).HLoc;
8563  ExitPair.second = Track->TrackElementAt(1420, MidElement).VLoc;
8564  Utilities->CallLogPop(2344);
8565  return(-1);
8566  }
8567  else //front element of train fully off the exit, one length to exit
8568  {
8569  TempTTE = (Track->TrackElementAt(1421, MidElement).Length01) * 3.6 / 60 / ExitSpeedFull;
8570  if(TempTTE < LastTimeToExit)
8571  {
8572  TimeToExit = TempTTE; //else leave as is, don't want a sudden increase in time
8573  }
8574  else
8575  {
8576  TimeToExit = LastTimeToExit;
8577  }
8578  ExitPair.first = Track->TrackElementAt(1422, MidElement).HLoc;
8579  ExitPair.second = Track->TrackElementAt(1423, MidElement).VLoc;
8580  Utilities->CallLogPop(2345);
8581  return(-1);
8582  }
8583  }
8584  if(LeadElement > -1)
8585  {
8587  && (ExitSpeedFull > 1)) //LeadElement is the exit. If LeadMidLag have lead half on exit or if LeadMid have lead full on exit
8588  { //if LeadMidLag have 2.5 lengths to exit, else have 2 lengths to exit
8589  if(Straddle == LeadMidLag)
8590  {
8591  TempTTE = (2.5 * Track->TrackElementAt(1426, LeadElement).Length01) * 3.6 / 60 / ExitSpeedFull;
8592  if(TempTTE < LastTimeToExit)
8593  {
8594  TimeToExit = TempTTE; //else leave as is, don't want a sudden increase in time
8595  }
8596  else
8597  {
8598  TimeToExit = LastTimeToExit;
8599  }
8600  ExitPair.first = Track->TrackElementAt(1427, LeadElement).HLoc;
8601  ExitPair.second = Track->TrackElementAt(1428, LeadElement).VLoc;
8602  Utilities->CallLogPop(2346);
8603  return(-1);
8604  }
8605  else
8606  {
8607  TempTTE = (2 * Track->TrackElementAt(1429, LeadElement).Length01) * 3.6 / 60 / ExitSpeedFull;
8608  if(TempTTE < LastTimeToExit)
8609  {
8610  TimeToExit = TempTTE; //else leave as is, don't want a sudden increase in time
8611  }
8612  else
8613  {
8614  TimeToExit = LastTimeToExit;
8615  }
8616  ExitPair.first = Track->TrackElementAt(1430, LeadElement).HLoc;
8617  ExitPair.second = Track->TrackElementAt(1431, LeadElement).VLoc;
8618  Utilities->CallLogPop(2347);
8619  return(-1);
8620  }
8621  }
8622  }
8623 //here if LeadElement > -1 and its forward connection also > -1
8624 
8625  // calc distance to next red signal
8626  if(!Stopped() || StoppedAtLocation)
8627  {
8628  int FirstPosToBeMeasured = Track->TrackElementAt(953, LeadElement).Conn[LeadExitPos];
8629  int FirstEntryPos = Track->TrackElementAt(954, LeadElement).ConnLinkPos[LeadExitPos];
8630  if((Straddle == LeadMidLag) && (TrainMode == Timetable))
8631 /* In TTMode it's important to set the first element to be measured ahead of the lead element only when the train fully on
8632  2 elements. Otherwise, if the train is only half on the lead element and approaching a station stop where the platform doesn't
8633  extend beyond the lead element stop point, the element ahead of the lead element is not a location whereas the ActionVector
8634  still points to the station stop location. In these circumstances the train hasn't yet stopped, so the dwell time at the
8635  stop isn't calculated, and the station to be stopped at isn't found as a future stop and nor are any other future stops
8636  because the ActionVector name never matches a future station. Hence all dwell times are omitted until the train lands fully
8637  on two elements. To avoid this when Straddle is LeadMidLag the first element to be measured is set to the lead element, so
8638  before the train has stopped the current station is still recognised as a future stop.
8639  In signaller mode stops don't count, and if pass red signal command is given then when have LeadMidLag the current element
8640  becomes the signal, and the time to act indication becomes 'NOW'.
8641 */
8642  {
8643  FirstPosToBeMeasured = LeadElement;
8644  FirstEntryPos = LeadEntryPos;
8645  }
8646  float CurrentStopTime; // set to 0 at start of function
8647  float LaterStopTime; // set to 0 at start of function
8648  float RecoverableTime; // set to 0 at start of function
8649  int AvTrackSpeed; // set to zero at start of function
8650  bool SigControlAndCanPassRedSignal = ((TrainMode == Signaller) && AllowedToPassRedSignal);
8651  DistanceToRedSignal = TrainController->CalcDistanceToRedSignalandStopTime(0, FirstPosToBeMeasured, FirstEntryPos, SigControlAndCanPassRedSignal,
8652  ActionVectorEntryPtr, HeadCode, TrainID, CurrentStopTime, LaterStopTime, RecoverableTime, AvTrackSpeed, DistanceToExit, ExitPair);
8653 //at this point can't have both DistanceToRedSignal and DistanceToExit both set. Either both will be unset (-1) or one will be set.
8654 //Therefore since need to calculate the time for each in the same way use a generic
8655 
8656  if((DistanceToRedSignal == -1) && (DistanceToExit == -1))// both unset so no action needed
8657  {
8658  TimeToExit = -1;
8659  Utilities->CallLogPop(2076);
8660  return(-1);
8661  }
8662 //else one or other is set
8663  bool DistanceToRedSignalSet = (DistanceToRedSignal > -1);
8664  bool DistanceToExitSet = (DistanceToExit > -1);
8665  int GenericDistance = DistanceToRedSignal;
8666  if(DistanceToExitSet)
8667  {
8668  GenericDistance = DistanceToExit;
8669  }
8670 /* Have MinsDelayed; pos or neg,
8671  CurrentStopTime; pos or zero
8672  LaterStopTime; pos or zero
8673  RecoverableTime; pos or zero
8674 
8675  & from these calculate TotalStopTime. noting that:
8676  If stopped CurrentStopTime automatically adjusts for all early running and for as much late running as possible
8677  RecoverableTime always < LaterStopTime or both zero
8678  can't subtract more than RecoverableTime (MinsDelayed > 0)
8679  only subtract from LaterStopTime, not CurrentTime (MinsDelayed > 0)
8680  only subtract from LaterStopTime if LaterStopTime > 0 (MinsDelayed > 0)
8681  only add to LaterStopTime if LaterStopTime > 0 (MinsDelayed < 0)
8682  if running early & stopped at location CurrentStopTime will automatically include the excess
8683 */
8684  float TimeToSubtract, TotalStopTime;
8685  if(MinsDelayed > RecoverableTime)
8686  {
8687  TimeToSubtract = RecoverableTime;
8688  }
8689  else
8690  {
8691  TimeToSubtract = MinsDelayed; // may be negative;
8692  }
8693  if((AvTrackSpeed > 0) && (DistanceToStationStop <= GenericDistance) && (DistanceToStationStop > 0)) //protection against div by zero, not needed of no stop
8694  //before red signal, DistanceToStationStop != 0 as set to 0 if invalid
8695  //added at v2.6.1, DistanceToStationStop is calculated in SetTrainMovementValues, AvTrackSpeed is average to next red signal, but should be ok to use for
8696  //next station stop
8697  //after 2.7.0 changed (DistanceToStationStop < DistanceToRedSignal) to (DistanceToStationStop <= DistanceToRedSignal) because often have departure signal
8698  //next to the stop platform and if so the two distances are the same and the station stop time isn't included. Also after 2.7.0 used GetRepeatTime... to calc
8699  //departure time because ActionVectorEntryPtr->DepartureTime is the base time and therefore incorrect for repeats.
8700  //first find departure time from the next stop
8701  //at v2.9.0 changed MinsEarly calc to use ArrivalTime instead of departure time
8702  {
8703  if(ActionVectorEntryPtr->FormatType == TimeTimeLoc) //if already arrived then MinsEarly will be < 0 so becomes set to 0
8704  {
8707  MinsEarly = (double(ArrivalTime - TrainController->TTClockTime) * 86400 / 60) - (DistanceToStationStop * 3.6 / 60 / AvTrackSpeed);
8708  }
8709  else if((ActionVectorEntryPtr->FormatType == TimeLoc) && (ActionVectorEntryPtr->ArrivalTime != TDateTime(-1))) // not arrived yet
8710  {
8712  MinsEarly = (double(ArrivalTime - TrainController->TTClockTime) * 86400 / 60) - (DistanceToStationStop * 3.6 / 60 / AvTrackSpeed);
8713  if((ActionVectorEntryPtr + 1)->FormatType == TimeLoc)
8714  {
8715  // must be a departure
8716  DepartureTime = TrainController->GetRepeatTime(70, (ActionVectorEntryPtr + 1)->DepartureTime, RepeatNumber, IncrementalMinutes);
8717  }
8718  }
8719  else if((ActionVectorEntryPtr->FormatType == TimeLoc) && (ActionVectorEntryPtr->ArrivalTime == TDateTime(-1))) //already arrived
8720  {
8721  MinsEarly = 0;
8722  }
8723  if(MinsEarly < 0)
8724  {
8725  MinsEarly = 0;
8726  }
8727  }
8728  if(MinsDelayed < 0) // MinsDelayed < 0 means have arrived early at a station
8729  {
8730  if(CurrentStopTime > 0)
8731  {
8732  TotalStopTime = CurrentStopTime + LaterStopTime;
8733  }
8734  // stopped at loc, will depart on time
8735  else
8736  {
8737  TotalStopTime = LaterStopTime - MinsDelayed;
8738  }
8739  // not stopped, will depart on time at first later stop so add the delay
8740  }
8741  else if((MinsEarly > 0) && !Stopped()) //running early
8742  {
8743  TotalStopTime = LaterStopTime + MinsEarly;
8744  }
8745  else // on time or running late
8746  {
8747  if(LaterStopTime == 0)
8748  {
8749  TotalStopTime = CurrentStopTime;
8750  }
8751  // no later stops, if stopped now will depart as soon as possible,
8752  // if not stopped no stop times to add
8753  else
8754  {
8755  TotalStopTime = CurrentStopTime + LaterStopTime - TimeToSubtract; // later stops so deduct as much as can
8756  }
8757  }
8758  if(AvTrackSpeed < 30)
8759  {
8760  AvTrackSpeed = 30;
8761  }
8762  int Speed = AvTrackSpeed;
8763  if(AvTrackSpeed > int(MaxRunningSpeed))
8764  {
8765  Speed = int(MaxRunningSpeed);
8766  }
8767  if(TrainMode == Signaller)
8768  {
8769  Speed = SignallerMaxSpeed;
8770  TotalStopTime = 0;
8771  }
8772  if(DistanceToRedSignalSet)
8773  {
8774  TimeToAct = TotalStopTime + GenericDistance * 3.6 / 60 / Speed;
8775  // accel & decel taken into account in
8776  // CalcDistanceToRedSignalandStopTime
8777  // 3.6 convertsKm/h to m/s & 60 converts seconds to minutes
8778  TimeToExit = -1;
8779  Utilities->CallLogPop(2079);
8780  return(TimeToAct);
8781  }
8782  else //DistanceToExitSet must be true
8783  {
8784  TimeToExit = TotalStopTime + GenericDistance * 3.6 / 60 / Speed;
8785  // accel & decel taken into account in
8786  // CalcDistanceToRedSignalandStopTime
8787  // 3.6 convertsKm/h to m/s & 60 converts seconds to minutes
8788  Utilities->CallLogPop(2370);
8789  return(-1); //no red signal so no time to act
8790  }
8791  }
8792  else // stopped not at location
8793  {
8795  {
8796  TimeToAct = 0.0;
8797  TimeToExit = -1;
8798  }
8799  // but if stopped at a signal & autosigs route after it then ok
8800  if(StoppedAtSignal)
8801  {
8802  int NextElement = Track->TrackElementAt(928, LeadElement).Conn[LeadExitPos];
8803  int NextEntryPos = Track->TrackElementAt(929, LeadElement).ConnLinkPos[LeadExitPos];
8804  int NextExitPos;
8805  if(Track->TrackElementAt(930, NextElement).TrackType == Points)
8806  {
8807  if((NextEntryPos == 0) || (NextEntryPos == 2))
8808  // leading entry point
8809  {
8810  if(Track->TrackElementAt(931, NextElement).Attribute == 0)
8811  {
8812  NextExitPos = 1;
8813  }
8814  else
8815  {
8816  NextExitPos = 3;
8817  }
8818  }
8819  else
8820  {
8821  NextExitPos = 0; // trailing entry point
8822  }
8823  }
8824  else
8825  {
8826  NextExitPos = Track->GetNonPointsOppositeLinkPos(NextEntryPos);
8827  }
8828  int NextButOneElement = Track->TrackElementAt(932, NextElement).Conn[NextExitPos];
8829  int NextButOneEntryPos = Track->TrackElementAt(933, NextElement).ConnLinkPos[NextExitPos];
8830  int RouteNumber; // holder for referenced value, not used
8831  if(AllRoutes->GetRouteTypeAndNumber(32, NextButOneElement, NextButOneEntryPos, RouteNumber) == TAllRoutes::AutoSigsRoute)
8832  {
8833  TimeToAct = -1;
8834  TimeToExit = -1;
8835  }
8836  }
8837  Utilities->CallLogPop(2074);
8838  return(TimeToAct);
8839  }
8840 }
8841 
8842 // ---------------------------------------------------------------------------
8843 
8845 {
8846  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TrainOnContinuation, " + HeadCode);
8847  if(LeadElement > -1)
8848  {
8850  {
8851  Utilities->CallLogPop(2148);
8852  return(true);
8853  }
8854  }
8855  if(MidElement > -1)
8856  {
8858  {
8859  Utilities->CallLogPop(2149);
8860  return(true);
8861  }
8862  }
8863  if(LagElement > -1)
8864  {
8866  {
8867  Utilities->CallLogPop(2150);
8868  return(true);
8869  }
8870  }
8871  Utilities->CallLogPop(2151);
8872  return(false);
8873 }
8874 
8875 // ---------------------------------------------------------------------------
8876 // TTrainController
8877 // ---------------------------------------------------------------------------
8878 
8880 {
8881  OnTimeArrivals = 0;
8882  LateArrivals = 0;
8883  EarlyArrivals = 0;
8884  OnTimePasses = 0;
8885  LatePasses = 0;
8886  EarlyPasses = 0;
8887  OnTimeExits = 0; //these 3 exits added at v2.9.2 - missed in error earlier
8888  LateExits = 0;
8889  EarlyExits = 0;
8890  OnTimeDeps = 0;
8891  LateDeps = 0;
8892  MissedStops = 0;
8893  OtherMissedEvents = 0;
8894  UnexpectedExits = 0;
8895  NumFailures = 0;
8896  IncorrectExits = 0;
8897  SPADEvents = 0;
8898  SPADRisks = 0;
8899  CrashedTrains = 0;
8900  Derailments = 0;
8901  TotArrDepPass = 0;
8902  TotLateArrMins = 0;
8903  TotEarlyArrMins = 0;
8904  TotLatePassMins = 0;
8905  TotEarlyPassMins = 0;
8906  TotLateExitMins = 0; //added at v2.9.1
8907  TotEarlyExitMins = 0; //added at v2.9.1
8908  TotLateDepMins = 0;
8909  ExcessLCDownMins = 0;
8910  SkippedTTEvents = 0; //added at v2.11.0
8911  TTClockTime = 0; // added for v0.6
8913  // added at v1.3.0 to ensure false at start
8914  OpTimeToActUpdateCounter = 0; // new v2.2.0
8915  OpActionPanelVisible = false; // new v2.2.0
8916  // reset all message flags, stops them being given twice (shouldn't be needed here but add for safety) //new at v2.4.0
8917  SSHigh = false;
8918  MRSHigh = false;
8919  MRSLow = false;
8920  MassHigh = false;
8921  BFHigh = false;
8922  BFLow = false;
8923  PwrHigh = false;
8924  SigSHigh = false;
8925  SigSLow = false;
8926  randomize();
8927  // to seed rand() & random() with a random number (see UpdateTrain)
8928 }
8929 
8930 // ---------------------------------------------------------------------------
8931 
8933 {
8934  for(unsigned int x = 0; x < TrainVector.size(); x++)
8935  {
8936  TrainVectorAt(32, x).DeleteTrain(4);
8937  }
8938  TrainVector.clear();
8939 }
8940 
8941 // ---------------------------------------------------------------------------
8942 
8943 void TTrainController::LogEvent(AnsiString Str)
8944 {
8945  AnsiString FullStr = Utilities->TimeStamp() + "," + TTClockTime.FormatString("hh:nn:ss") + "," + Str;
8946 
8947  // restrict to last 1000 entries
8948  Utilities->EventLog.push_back(FullStr);
8949  if(Utilities->EventLog.size() > 1000)
8950  {
8951  Utilities->EventLog.pop_front();
8952  }
8953 }
8954 
8955 // ---------------------------------------------------------------------------
8956 
8958 {
8959  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",Operate");
8960  bool ClockState = Utilities->Clock2Stopped;
8961 
8962  Utilities->Clock2Stopped = true;
8963  // new section dealing with Snt & Snt-sh additions
8964  // BUT don't add trains if points or route flashing [conditions added for Version 0.6 as a result of Najamuddin's error - 15/01/11] - wait until next
8965  // clock tick after stops flashing
8967  {
8968  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
8969  {
8970  TTrainDataEntry & TDEntry = TrainDataVector.at(x);
8971  const TActionVectorEntry &AVEntry0 = TDEntry.ActionVector.at(0);
8972  TActionEventType EventType = NoEvent;
8973  if(AVEntry0.Command == "Snt")
8974  {
8975  // calc below only for Snt & Snt-sh entries rather than all entries to save time
8976  const TActionVectorEntry &AVEntryLast = TDEntry.ActionVector.at(TDEntry.ActionVector.size() - 1);
8977  int IncrementalMinutes = 0;
8978  int IncrementalDigits = 0;
8979  if(AVEntryLast.FormatType == Repeat)
8980  {
8981  IncrementalMinutes = AVEntryLast.RearStartOrRepeatMins;
8982  IncrementalDigits = AVEntryLast.FrontStartOrRepeatDigits;
8983  }
8984  if((AVEntryLast.FormatType == Repeat) && (TDEntry.NumberOfTrains < 2))
8985  {
8986  throw Exception("Error - Repeat entry && less than two trains for Snt entry: " + TDEntry.HeadCode);
8987  }
8988  // see above note
8989 
8990  for(int y = 0; y < TDEntry.NumberOfTrains; y++)
8991  {
8992  TTrainOperatingData &TTOD = TDEntry.TrainOperatingDataVector.at(y);
8993  if(TTOD.RunningEntry != NotStarted)
8994  {
8995  continue;
8996  }
8997 
8998 //Multiplayer: here check for a train entering at a coupling {RearStartOrRepeatMins shows if it's a coupling or not), and can only be a Snt entry
8999 //if so and no arrival signalled yet bypass the timetabled arrival
9000 //if so and arrival signalled then start the new service, using the repeat number and headcode for the entering train
9001 //if a repeat is skipped then should be ok if it arrives later as its RunningEntry is still NotStarted
9002 
9003  if(GetRepeatTime(2, AVEntry0.EventTime, y, IncrementalMinutes) > TTClockTime)
9004  {
9005  break; // all the rest will also be greater
9006  }
9007  AnsiString TrainHeadCode = GetRepeatHeadCode(22, TDEntry.HeadCode, y, IncrementalDigits);
9008  if(AddTrain(2, AVEntry0.RearStartOrRepeatMins, AVEntry0.FrontStartOrRepeatDigits, TrainHeadCode, TDEntry.StartSpeed, TDEntry.Mass,
9009  TDEntry.MaxRunningSpeed, TDEntry.MaxBrakeRate, TDEntry.PowerAtRail, "Timetable", &TDEntry, y, IncrementalMinutes, IncrementalDigits,
9010  TDEntry.SignallerSpeed, AVEntry0.SignallerControl, EventType))
9011  {
9012  TTOD.TrainID = TrainVector.back().TrainID;
9013  TTOD.RunningEntry = Running;
9014  }
9015  else if(EventType == FailTrainEntry)
9016  {
9017  break; // if a train can't enter no point checking any more repeats as they won't be able to enter either
9018  }
9019  }
9020  }
9021  if(AVEntry0.Command == "Snt-sh")
9022  // just start this once, shuttle repeats take care of restarts
9023  {
9024  // calc below only for Snt & Snt-sh entries rather than all entries to save time
9025  const TActionVectorEntry &AVEntryLast = TDEntry.ActionVector.at(TDEntry.ActionVector.size() - 1);
9026  int IncrementalMinutes = 0;
9027  int IncrementalDigits = 0;
9028  if(AVEntryLast.FormatType == Repeat)
9029  {
9030  IncrementalMinutes = AVEntryLast.RearStartOrRepeatMins;
9031  IncrementalDigits = AVEntryLast.FrontStartOrRepeatDigits;
9032  }
9033  if((AVEntryLast.FormatType == Repeat) && (TDEntry.NumberOfTrains < 2))
9034  {
9035  throw Exception("Error - Repeat entry && less than two trains for Snt-sh entry: " + TDEntry.HeadCode);
9036  }
9037  // see above note
9038  TTrainOperatingData &TTOD = TDEntry.TrainOperatingDataVector.at(0);
9039  if(TTOD.RunningEntry == NotStarted)
9040  {
9041  if(AVEntry0.EventTime <= TTClockTime)
9042  {
9043  if(AddTrain(3, AVEntry0.RearStartOrRepeatMins, AVEntry0.FrontStartOrRepeatDigits, TDEntry.HeadCode, TDEntry.StartSpeed, TDEntry.Mass,
9044  TDEntry.MaxRunningSpeed, TDEntry.MaxBrakeRate, TDEntry.PowerAtRail, "Timetable", &TDEntry, 0, IncrementalMinutes, IncrementalDigits,
9045  TDEntry.SignallerSpeed, false, EventType))
9046  // false for SignallerControl
9047  {
9048  TTOD.TrainID = TrainVector.back().TrainID;
9049  TTOD.RunningEntry = Running;
9050  }
9051  else if(EventType == FailTrainEntry)
9052  {
9053  break; // if a train can't enter no point checking any more repeats as they won't be able to enter either
9054  }
9055  }
9056  }
9057  }
9058  }
9059  }
9060  // deal with running trains but abort if any vectors added, would probably be OK but don't risk a vector reallocation disrupting the
9061  // iteration, next cycle will catch up with any other pending updates
9062  if(!TrainVector.empty())
9063  {
9064  TrainAdded = false;
9065  AllRoutes->CallonVector.clear();
9066  // this will be rebuilt during the calls to UpdateTrain
9067  for(unsigned int x = 0; x < TrainVector.size(); x++)
9068  {
9069  TrainVectorAt(33, x).UpdateTrain(0);
9070  if(TrainAdded || TrainVectorAt(35, x).HasTrainGone())
9071  // added HasTrainGone() condition in v0.4c to prevent 2 trains both having TrainGone set
9072  // at the same time. That caused the error Craig Weekes reported in November 2010 where 2 trains exited at the same time, and later the TrainVector
9073  // iterates in reverse to erase the second train to have gone, but afterwards ReplotTrains iterates forwards and therefore replots the first train to
9074  // have gone and therefore sets the TrainIDOnElement value to the exited train, with nothing to reset it. Hovering the mouse over that element with
9075  // train information enabled causes an error because the track element thinks the train is still there, whereas it is missing from the TrainVector.
9076  {
9077  break;
9078  }
9079  }
9080  // set warning flags
9081  CrashWarning = false;
9082  DerailWarning = false;
9083  SPADWarning = false;
9084  CallOnWarning = false;
9085  SignalStopWarning = false;
9086  BufferAttentionWarning = false;
9087  TrainFailedWarning = false;
9088  for(int x = TrainVector.size() - 1; x >= 0; x--) // reverse because of erase
9089  {
9090  TTrain &Train = TrainVectorAt(34, x);
9091  if(Train.Crashed)
9092  // can't use background colours for crashed & derailed because same colour
9093  {
9094  CrashWarning = true;
9095  }
9096  else if(Train.Derailed)
9097  // can't use background colours for crashed & derailed because same colour
9098  {
9099  DerailWarning = true;
9100  }
9101  else if(Train.BackgroundColour == clSPADBackground)
9102  // use colour as that changes as soon as passes signal
9103  {
9104  SPADWarning = true;
9105  }
9106  else if(Train.BackgroundColour == clTrainFailedBackground)
9107  {
9108  TrainFailedWarning = true;
9109  }
9110  else if(Train.BackgroundColour == clCallOnBackground)
9111  // use colour as also stopped at signal
9112  {
9113  CallOnWarning = true;
9114  }
9115  else if(Train.BackgroundColour == clSignalStopBackground)
9116  // use colour to distinguish from call-on
9117  {
9118  SignalStopWarning = true;
9119  }
9120  else if(Train.BackgroundColour == clBufferAttentionNeeded)
9121  // use colour to distinguish from ordinary buffer stop
9122  {
9123  BufferAttentionWarning = true;
9124  }
9125  if(Train.HasTrainGone())
9126  {
9127  AnsiString Loc = "";
9128  bool ElementFound = false;
9129  TTrackElement TE;
9130  if(Train.LagElement > -1)
9131  {
9132  TE = Track->TrackElementAt(531, Train.LagElement);
9133  ElementFound = true;
9134  }
9135  else if(Train.MidElement > -1)
9136  {
9137  TE = Track->TrackElementAt(779, Train.MidElement);
9138  ElementFound = true;
9139  }
9140  else if(Train.LeadElement > -1)
9141  {
9142  TE = Track->TrackElementAt(780, Train.LeadElement);
9143  ElementFound = true;
9144  }
9145  if(ElementFound)
9146  {
9147  if(TE.ActiveTrackElementName != "")
9148  {
9149  Loc = TE.ActiveTrackElementName + ", track element " + TE.ElementID;
9150  }
9151  else
9152  {
9153  Loc = "track element " + TE.ElementID;
9154  }
9155  }
9156  TActionVectorEntry *AVEntryPtr = Train.ActionVectorEntryPtr;
9157  if((Train.SignallerRemoved) || (Train.JoinedOtherTrainFlag))
9158  // need above first because may also have ActionVectorEntryPtr == "Fer"
9159  {
9160  Train.UnplotTrain(9);
9161  // added at v1.3.0 to reset signals after train removed from an autosigsroute
9163  {
9166  }
9167  // end of addition
9168  AllRoutes->RebuildRailwayFlag = true;
9169  // to force ClearandRebuildRailway at next clock tick if not in zoom-out mode, to replot LCs
9170  // correctly after a crash
9171  }
9172  else if(AVEntryPtr->Command == "Fer")
9173  {
9174  bool CorrectExit = false;
9175  if(!AVEntryPtr->ExitList.empty())
9176  {
9177  for(TNumListIterator ELIT = AVEntryPtr->ExitList.begin(); ELIT != AVEntryPtr->ExitList.end(); ELIT++)
9178  {
9179  if(*ELIT == Train.LagElement)
9180  {
9181  CorrectExit = true;
9182  }
9183  }
9184  }
9185  if(CorrectExit)
9186  {
9187  Train.LogAction(19, Train.HeadCode, "", Leave, Loc, AVEntryPtr->EventTime, AVEntryPtr->Warning);
9188  }
9189  else
9190  {
9191  LogActionError(38, Train.HeadCode, "", FailIncorrectExit, Loc);
9192  }
9193  }
9194  else
9195  {
9196  if(!AVEntryPtr->SignallerControl)
9197  {
9198  LogActionError(26, Train.HeadCode, "", FailUnexpectedExitRailway, Loc);
9199  Train.SendMissedActionLogs(2, -2, AVEntryPtr);
9200  // -2 is marker for send messages for all remaining actions except Fer if present
9201  }
9202  else
9203  {
9204  Train.LogAction(31, Train.HeadCode, "", SignallerLeave, Loc, TDateTime(0), false); // false for Warning
9205  }
9206  }
9207  Train.TrainDataEntryPtr->TrainOperatingDataVector.at(Train.RepeatNumber).RunningEntry = Exited;
9208  Train.DeleteTrain(1);
9209  TrainVector.erase(TrainVector.begin() + x);
9210  ReplotTrains(1, Display);
9211  // to reset ElementIDs for remaining trains when have removed a train
9212  }
9213  }
9214  }
9215  else
9216  {
9217  // reset all flags in case last train removed with flag set
9218  CrashWarning = false;
9219  DerailWarning = false;
9220  SPADWarning = false;
9221  CallOnWarning = false;
9222  SignalStopWarning = false;
9223  BufferAttentionWarning = false;
9224  TrainFailedWarning = false;
9225  }
9226  // update OpTimeToActMultimap
9228  {
9230  // clears entries then adds values for running trains then for continuation entries
9232  //added for multiplayer for running trains only
9233  }
9234  Utilities->Clock2Stopped = ClockState;
9235  Utilities->CallLogPop(723);
9236 }
9237 
9238 // ---------------------------------------------------------------------------
9240 {
9241  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",FinishedOperation");
9242  if(!TrainVector.empty())
9243  {
9244  for(int x = TrainVector.size() - 1; x >= 0; x--)
9245  {
9246  TrainVectorAt(50, x).DeleteTrain(2);
9247  }
9248  TrainVector.clear();
9249  }
9250  if(!TrainDataVector.empty())
9251  {
9252  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
9253  {
9254  TTrainDataEntry &TDEntry = TrainDataVector.at(x);
9255  for(int y = 0; y < TDEntry.NumberOfTrains; y++)
9256  {
9257  TTrainOperatingData &TOD = TDEntry.TrainOperatingDataVector.at(y);
9258  TOD.RunningEntry = NotStarted;
9259  TOD.TrainID = -1;
9260  TOD.EventReported = NoEvent;
9261  }
9262  }
9263  }
9264  Display->GetOutputLog1()->Caption = "";
9265  Display->GetOutputLog2()->Caption = "";
9266  Display->GetOutputLog3()->Caption = "";
9267  Display->GetOutputLog4()->Caption = "";
9268  Display->GetOutputLog5()->Caption = "";
9269  Display->GetOutputLog6()->Caption = "";
9270  Display->GetOutputLog7()->Caption = "";
9271  Display->GetOutputLog8()->Caption = "";
9272  Display->GetOutputLog9()->Caption = "";
9273  Display->GetOutputLog10()->Caption = "";
9274  Utilities->CallLogPop(1352);
9275 }
9276 
9277 // ---------------------------------------------------------------------------
9278 
9280 {
9281  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ReplotTrains");
9282  if(!TrainVector.empty())
9283  {
9284  for(unsigned int x = 0; x < TrainVector.size(); x++)
9285  {
9286  TrainVectorAt(51, x).PlotTrain(4, Disp);
9287  }
9288  }
9289  Utilities->CallLogPop(724);
9290 }
9291 
9292 // ---------------------------------------------------------------------------
9293 
9294 void TTrainController::WriteTrainsToImage(int Caller, Graphics::TBitmap *Bitmap)
9295 {
9296  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",WriteTrainsToImage");
9297  if(!TrainVector.empty())
9298  {
9299  for(unsigned int x = 0; x < TrainVector.size(); x++)
9300  {
9301  TrainVectorAt(61, x).WriteTrainToImage(0, Bitmap);
9302  }
9303  }
9304  Utilities->CallLogPop(1707);
9305 }
9306 
9307 // ---------------------------------------------------------------------------
9308 
9310 {
9311  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",UnplotTrains");
9312  if(!TrainVector.empty())
9313  {
9314  for(unsigned int x = 0; x < TrainVector.size(); x++)
9315  {
9316  TrainVectorAt(52, x).UnplotTrain(10);
9317  }
9318  }
9320  Utilities->CallLogPop(725);
9321 }
9322 
9323 // ---------------------------------------------------------------------------
9324 
9325 bool TTrainController::AddTrain(int Caller, int RearPosition, int FrontPosition, AnsiString HeadCode, int StartSpeed, int Mass, double MaxRunningSpeed,
9326  double MaxBrakeRate, double PowerAtRail, AnsiString ModeStr, TTrainDataEntry *TrainDataEntryPtr, int RepeatNumber, int IncrementalMinutes,
9327  int IncrementalDigits, int SignallerSpeed, bool SignallerControl, TActionEventType &EventType)
9328 {
9329  LogEvent(AnsiString(Caller) + ",AddTrain," + AnsiString(RearPosition) + "," + AnsiString(FrontPosition) + "," + HeadCode + "," + AnsiString(StartSpeed) +
9330  "," + AnsiString(Mass) + "," + ModeStr);
9331  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",AddTrain," + AnsiString(RearPosition) + "," + AnsiString(FrontPosition) +
9332  "," + HeadCode + "," + AnsiString(StartSpeed) + "," + AnsiString(Mass) + "," + ModeStr + "," + HeadCode);
9333 
9334  int RearExitPos = -1;
9335 
9336  for(int x = 0; x < 4; x++)
9337  {
9338  if(Track->TrackElementAt(519, RearPosition).Conn[x] == FrontPosition)
9339  {
9340  RearExitPos = x;
9341  }
9342  }
9343  if(RearExitPos == -1)
9344  {
9345  throw Exception("Error, RearExit == -1 in AddTrain");
9346  }
9347  bool ReportFlag = true;
9348 
9349  // used to stop repeated messages from CheckStartAllowable when split failed
9350  if(TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).EventReported != NoEvent)
9351  {
9352  ReportFlag = false;
9353  }
9354  if(!CheckStartAllowable(0, RearPosition, RearExitPos, HeadCode, ReportFlag, EventType))
9355  {
9356  // messages sent to performance log in CheckStartAllowable if ReportFlag true
9357  TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).EventReported = EventType;
9358  Utilities->CallLogPop(938);
9359  return(false);
9360  }
9361  TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).EventReported = NoEvent;
9362  TTrainMode TrainMode = NoMode;
9363 
9364  if(ModeStr == "Timetable")
9365  {
9366  TrainMode = Timetable;
9367  }
9368  // all else gives 'None', 'Signaller' set within program
9369 
9370  if(MaxRunningSpeed < 10)
9371  {
9372  MaxRunningSpeed = 10; // added at v0.6 to avoid low max speeds
9373  }
9374  if(SignallerSpeed < 10)
9375  {
9376  SignallerSpeed = 10; // added at v0.6 to avoid low max speeds
9377  }
9378  TTrain *NewTrain = new TTrain(0, RearPosition, RearExitPos, HeadCode, StartSpeed, Mass, MaxRunningSpeed, MaxBrakeRate, PowerAtRail, TrainMode,
9379  TrainDataEntryPtr, RepeatNumber, IncrementalMinutes, IncrementalDigits, SignallerSpeed);
9380 
9381  NewTrain->ActionVectorEntryPtr = &(TrainDataEntryPtr->ActionVector.at(0));
9382  // initialise here rather than in TTrain constructor as create trains
9383  // with Null TrainDataEntryPtr when loading session trains
9384  if(SignallerControl)
9385  {
9386  NewTrain->TimetableFinished = true;
9387  NewTrain->SignallerStoppingFlag = false;
9388  NewTrain->TrainMode = Signaller;
9389  if(NewTrain->MaxRunningSpeed > NewTrain->SignallerMaxSpeed)
9390  {
9391  NewTrain->MaxRunningSpeed = NewTrain->SignallerMaxSpeed;
9392  }
9394  }
9395  // deal with starting conditions:-
9396  // unlocated Snt: just report entry & advance pointer
9397  // located Snt or Sfs: set station conditions as would if had reached stop point in Update(), & advance the ActionVectorEntryPtr
9398  // Sns doesn't need a new train
9399  if(NewTrain->ActionVectorEntryPtr->LocationName != "")
9400  // covers all above located starts
9401  // if location of Snt was a station (that is set as LocationName, i.e. not just any station) that isn't next departure station then
9402  // wouldn't have accepted the timetable
9403  {
9404  // first check if LeadElement (can't access LeadElement directly yet as not set, use FrontPosition instead) is buffers, note that
9405  // StoppedAtBuffers is set in UpdateTrain()
9406  if(Track->TrackElementAt(520, FrontPosition).TrackType == Buffers)
9407  // buffer end must be ahead of train or would have failed start position check
9408  {
9409  NewTrain->StoppedAtLocation = true;
9410  NewTrain->PlotStartPosition(0);
9412  NewTrain->LogAction(20, NewTrain->HeadCode, "", Create, NewTrain->ActionVectorEntryPtr->LocationName, NewTrain->ActionVectorEntryPtr->EventTime,
9413  NewTrain->ActionVectorEntryPtr->Warning);
9414  if(!SignallerControl) // don't advance if SignalControlEntry
9415  {
9416  NewTrain->ActionVectorEntryPtr++;
9417  // should be a command, could be a location departure but if so can't depart so set 'Hold' anyway
9418  }
9419  NewTrain->LastActionTime = TTClockTime;
9420  }
9421  // else a through station stop
9422  else
9423  {
9424  NewTrain->StoppedAtLocation = true;
9425  NewTrain->PlotStartPosition(10);
9427  NewTrain->LogAction(21, NewTrain->HeadCode, "", Create, NewTrain->ActionVectorEntryPtr->LocationName, NewTrain->ActionVectorEntryPtr->EventTime,
9428  NewTrain->ActionVectorEntryPtr->Warning);
9429  if(!SignallerControl) // don't advance if SignalControlEntry
9430  {
9431  NewTrain->ActionVectorEntryPtr++;
9432  }
9433  NewTrain->LastActionTime = TTClockTime;
9434  }
9435  }
9436  else // unlocated entry (i.e. not a stop entry, but could still be at a named location)
9437  {
9438  NewTrain->PlotStartPosition(11);
9439  TTrackElement TE = Track->TrackElementAt(530, NewTrain->RearStartElement);
9440  AnsiString Loc = "";
9441  if(TE.ActiveTrackElementName != "")
9442  {
9443  Loc = TE.ActiveTrackElementName + ", track element " + TE.ElementID;
9444  }
9445  else
9446  {
9447  Loc = "track element " + TE.ElementID;
9448  }
9449  if(TE.TrackType == Continuation)
9450  {
9451  NewTrain->LogAction(22, NewTrain->HeadCode, "", Enter, Loc, NewTrain->ActionVectorEntryPtr->EventTime, NewTrain->ActionVectorEntryPtr->Warning);
9452  }
9453  else
9454  {
9455  NewTrain->LogAction(23, NewTrain->HeadCode, "", Create, Loc, NewTrain->ActionVectorEntryPtr->EventTime, NewTrain->ActionVectorEntryPtr->Warning);
9456  }
9457  if(!SignallerControl) // don't advance if SignalControlEntry
9458  {
9459  NewTrain->ActionVectorEntryPtr++;
9460  }
9461  NewTrain->LastActionTime = TTClockTime;
9462  // no need to set LastActionTime for an unlocated entry
9463  }
9464  // cancel a wrong-direction route if either element of train starts on one
9465  if(NewTrain->LeadElement > -1)
9466  {
9467  NewTrain->CheckAndCancelRouteForWrongEndEntry(3, NewTrain->LeadElement, NewTrain->LeadEntryPos);
9468  }
9469  if(NewTrain->MidElement > -1)
9470  {
9471  NewTrain->CheckAndCancelRouteForWrongEndEntry(4, NewTrain->MidElement, NewTrain->MidEntryPos);
9472  }
9473  // set signals for a right-direction autosigs route for either element of train on one
9474  // erase elements back to start for a non-autosigs route & check if an autosigs route immediately behind it, and if so set its signals
9475  // note that all but autosigs routes become part of a single route, so there can only be an autosigs route behind the non-autosigs route
9476  int RouteNumber = -1;
9477  bool SignalsSet = false;
9478 
9479  if(NewTrain->LeadElement > -1)
9480  {
9481  if(AllRoutes->GetRouteTypeAndNumber(13, NewTrain->LeadElement, NewTrain->LeadEntryPos, RouteNumber) == TAllRoutes::AutoSigsRoute)
9482  {
9483  // below added in place of SetRouteSignals in v2.4.0 as don't want to set signals from start of route for a new train addition
9484  int RouteStartPosition;
9485  TAllRoutes::TRouteElementPair FirstPair, SecondPair;
9486  FirstPair = AllRoutes->GetRouteElementDataFromRoute2MultiMap(21, Track->TrackElementAt(955, FrontPosition).HLoc,
9487  Track->TrackElementAt(956, FrontPosition).VLoc, SecondPair);
9488  if(FirstPair.first == RouteNumber)
9489  {
9490  RouteStartPosition = FirstPair.second;
9491  }
9492  else if(SecondPair.first == RouteNumber)
9493  {
9494  RouteStartPosition = SecondPair.second;
9495  }
9496  else
9497  {
9498  throw Exception("Error, RouteNumber not found in Route2MultiMap in 1st of 2 calls to SetAllRearwardsSignals in AddTrain");
9499  }
9500  AllRoutes->SetAllRearwardsSignals(10, 0, RouteNumber, RouteStartPosition);
9501  SignalsSet = true;
9502  // AllRoutes->GetFixedRouteAt(, RouteNumber).SetRouteSignals(); above substituted in v2.4.0
9503  }
9504  else if(RouteNumber > -1) // non-autosigsroute
9505  {
9506  TPrefDirElement TempPDE = AllRoutes->GetFixedRouteAt(181, RouteNumber).GetFixedPrefDirElementAt(194, 0);
9507  int FirstTVPos = TempPDE.GetTrackVectorPosition();
9508  int FirstELinkPos = TempPDE.GetELinkPos();
9509  while(TempPDE.GetTrackVectorPosition() != (unsigned int)(NewTrain->LeadElement))
9510  {
9511  AllRoutes->RemoveRouteElement(16, TempPDE.HLoc, TempPDE.VLoc, TempPDE.GetELink());
9512  TempPDE = AllRoutes->GetFixedRouteAt(182, RouteNumber).GetFixedPrefDirElementAt(195, 0);
9513  }
9514  if(TempPDE.GetTrackVectorPosition() == (unsigned int)(NewTrain->LeadElement))
9515  {
9516  AllRoutes->RemoveRouteElement(17, TempPDE.HLoc, TempPDE.VLoc, TempPDE.GetELink());
9517  // remove the last element under LeadElement
9518  }
9519  AllRoutes->RebuildRailwayFlag = true;
9520  // to force ClearandRebuildRailway at next clock tick if not in zoom-out mode
9521  // now deal with a rear linked autosigs route
9522  if(Track->TrackElementAt(820, FirstTVPos).Conn[FirstELinkPos] > -1)
9523  {
9524  int LinkedRouteNumber = -1;
9525  if(AllRoutes->GetRouteTypeAndNumber(17, Track->TrackElementAt(821, FirstTVPos).Conn[FirstELinkPos],
9526  Track->TrackElementAt(822, FirstTVPos).ConnLinkPos[FirstELinkPos], LinkedRouteNumber) == TAllRoutes::AutoSigsRoute)
9527  {
9528  AllRoutes->GetFixedRouteAt(169, LinkedRouteNumber).SetRouteSignals(0);
9529  // this is ok as here we are setting signals from the start of the route
9530  }
9531  }
9532  SignalsSet = true;
9533  }
9534  }
9535  if(NewTrain->MidElement > -1)
9536  // if entering at a continuation MidElement == -1
9537  {
9538  // this is included in case a train starts with LeadElement on no route and MidElement on a route
9539  if(!SignalsSet)
9540  {
9541  RouteNumber = -1;
9542  if(AllRoutes->GetRouteTypeAndNumber(14, NewTrain->MidElement, NewTrain->MidEntryPos, RouteNumber) == TAllRoutes::AutoSigsRoute)
9543  {
9544  // below added in place of SetRouteSignals in v2.4.0 as don't want to set signals from start of route for a new train addition
9545  int RouteStartPosition;
9546  TAllRoutes::TRouteElementPair FirstPair, SecondPair;
9547  FirstPair = AllRoutes->GetRouteElementDataFromRoute2MultiMap(22, Track->TrackElementAt(957, RearPosition).HLoc,
9548  Track->TrackElementAt(958, RearPosition).VLoc, SecondPair);
9549  if(FirstPair.first == RouteNumber)
9550  {
9551  RouteStartPosition = FirstPair.second;
9552  }
9553  else if(SecondPair.first == RouteNumber)
9554  {
9555  RouteStartPosition = SecondPair.second;
9556  }
9557  else
9558  {
9559  throw Exception("Error, RouteNumber not found in Route2MultiMap in 2nd of 2 calls to SetAllRearwardsSignals in AddTrain");
9560  }
9561  AllRoutes->SetAllRearwardsSignals(11, 0, RouteNumber, RouteStartPosition);
9562  SignalsSet = true;
9563  // AllRoutes->GetFixedRouteAt(, RouteNumber).SetRouteSignals(); above substituted in v2.4.0
9564  }
9565  else if(RouteNumber > -1) // non-autosigsroute
9566  {
9567  TPrefDirElement TempPDE = AllRoutes->GetFixedRouteAt(184, RouteNumber).GetFixedPrefDirElementAt(196, 0);
9568  int FirstTVPos = TempPDE.GetTrackVectorPosition();
9569  int FirstELinkPos = TempPDE.GetELinkPos();
9570  while(TempPDE.GetTrackVectorPosition() != (unsigned int)(NewTrain->MidElement))
9571  {
9572  AllRoutes->RemoveRouteElement(18, TempPDE.HLoc, TempPDE.VLoc, TempPDE.GetELink());
9573  TempPDE = AllRoutes->GetFixedRouteAt(185, RouteNumber).GetFixedPrefDirElementAt(197, 0);
9574  }
9575  if(TempPDE.GetTrackVectorPosition() == (unsigned int)(NewTrain->MidElement))
9576  {
9577  AllRoutes->RemoveRouteElement(19, TempPDE.HLoc, TempPDE.VLoc, TempPDE.GetELink());
9578  // remove the last element under LeadElement
9579  }
9580  AllRoutes->RebuildRailwayFlag = true;
9581  // to force ClearandRebuildRailway at next clock tick if not in zoom-out mode
9582  // now deal with a rear linked autosigs route
9583  if(Track->TrackElementAt(823, FirstTVPos).Conn[FirstELinkPos] > -1)
9584  {
9585  int LinkedRouteNumber = -1;
9586  if(AllRoutes->GetRouteTypeAndNumber(19, Track->TrackElementAt(824, FirstTVPos).Conn[FirstELinkPos],
9587  Track->TrackElementAt(825, FirstTVPos).ConnLinkPos[FirstELinkPos], LinkedRouteNumber) == TAllRoutes::AutoSigsRoute)
9588  {
9589  AllRoutes->GetFixedRouteAt(170, LinkedRouteNumber).SetRouteSignals(1);
9590  // this is ok as now we are setting signals from the start of the route
9591  }
9592  }
9593  }
9594  }
9595  }
9596  TrainVector.push_back(*NewTrain);
9597  Utilities->CallLogPop(731);
9598  return(true);
9599 }
9600 
9601 // ---------------------------------------------------------------------------
9602 
9603 int TTrainController::EntryPos(int Caller, int TrainIDIn, int TrackVectorNumber)
9604 {
9605  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",EntryPos," + AnsiString(TrainIDIn) + "," +
9606  AnsiString(TrackVectorNumber));
9607  int VecPos = -1;
9608 
9609  for(unsigned int x = 0; x < TrainVector.size(); x++)
9610  {
9611  if(TrainVectorAt(1, x).TrainID == TrainIDIn)
9612  {
9613  VecPos = x;
9614  }
9615  }
9616  if(VecPos == -1)
9617  {
9618  throw Exception("Error, VecPos not set in EntryPos");
9619  }
9620  if(TrainVectorAt(2, VecPos).LeadElement == TrackVectorNumber)
9621  {
9622  Utilities->CallLogPop(734);
9623  return(TrainVectorAt(3, VecPos).LeadEntryPos);
9624  }
9625  else if(TrainVectorAt(4, VecPos).MidElement == TrackVectorNumber)
9626  {
9627  Utilities->CallLogPop(735);
9628  return(TrainVectorAt(5, VecPos).MidEntryPos);
9629  }
9630  else if(TrainVectorAt(6, VecPos).LagElement == TrackVectorNumber)
9631  {
9632  Utilities->CallLogPop(736);
9633  return(TrainVectorAt(7, VecPos).LagEntryPos);
9634  }
9635  Utilities->CallLogPop(737);
9636  return(-1);
9637 }
9638 
9639 // ---------------------------------------------------------------------------
9640 
9642 {
9643  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TrainVectorAtIdent," + AnsiString(TrainID));
9644  for(unsigned int x = 0; x < TrainVector.size(); x++)
9645  {
9646  if(TrainVectorAt(53, x).TrainID == TrainID)
9647  {
9648  Utilities->CallLogPop(738);
9649  return(TrainVectorAt(54, x));
9650  }
9651  }
9652  throw Exception("Error - No Train identified in TrainVectorAtIdent with ID = " + AnsiString(TrainID));
9653 }
9654 
9655 // ---------------------------------------------------------------------------
9656 
9657 bool TTrainController::TrainExistsAtIdent(int Caller, int TrainID)
9658 // return true if find the train (added at v2.4.0 as can select a removed train
9659 // in OAListBox before it updates - reported by LiWinDom in error report 23/04/20)
9660 {
9661  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TrainExistsAtIdent," + AnsiString(TrainID));
9662  for(unsigned int x = 0; x < TrainVector.size(); x++)
9663  {
9664  if(TrainVectorAt(69, x).TrainID == TrainID)
9665  {
9666  Utilities->CallLogPop(2152);
9667  return(true);
9668  }
9669  }
9670  Utilities->CallLogPop(2153);
9671  return(false);
9672 }
9673 
9674 // ---------------------------------------------------------------------------
9675 
9676 TDateTime TTrainController::GetControllerTrainTime(int Caller, TDateTime Time, int RepeatNumber, int IncrementalMinutes)
9677 {
9678  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetControllerTrainTime," + AnsiString(RepeatNumber) + "," +
9679  Utilities->Format96HHMMSS(Time));
9680  TDateTime RepeatTime = TrainController->GetRepeatTime(47, Time, RepeatNumber, IncrementalMinutes);
9681 
9682  Utilities->CallLogPop(2061);
9683  return(RepeatTime);
9684 }
9685 
9686 // ---------------------------------------------------------------------------
9687 
9688 AnsiString TTrainController::ContinuationEntryFloatingTTString(int Caller, TTrainDataEntry *TTDEPtr, int RepNum, int IncMins, int IncDig)
9689 // Enter with Ptr pointing to first action to be listed (i.e. next action)
9690 {
9691  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ContinuationEntryFloatingTTString" + "," + TTDEPtr->HeadCode);
9692  AnsiString RetStr = "", PartStr = "";
9693  int Count = 0;
9694  TActionVectorIterator Ptr = TTDEPtr->ActionVector.begin();
9695 
9696  Ptr--; // because incremented at start of loop
9697  do
9698  {
9699  Ptr++;
9700  if((Ptr->Command != "") && (Ptr->Command[1] == 'S'))
9701  {
9702  continue; // move past the starting entry
9703  }
9704  if((Ptr->FormatType == Repeat) || Ptr >= TTDEPtr->ActionVector.end())
9705  {
9706  break;
9707  }
9708  if(Ptr->SignallerControl)
9709  {
9710  RetStr = "Train under signaller control";
9711  break;
9712  }
9713  if(Ptr->FormatType == TimeTimeLoc)
9714  {
9715  if(Ptr->ArrivalTime == Ptr->DepartureTime)
9716  {
9717  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(0, Ptr->ArrivalTime, RepNum, IncMins)) + ": Arrive & depart from " + Ptr->LocationName;
9718  }
9719  else
9720  {
9721  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(1, Ptr->ArrivalTime, RepNum, IncMins)) + ": Arrive at " + Ptr->LocationName + '\n' +
9722  Utilities->Format96HHMM(GetControllerTrainTime(2, Ptr->DepartureTime, RepNum, IncMins)) + ": Depart from " + Ptr->LocationName;
9723  Count++; // because there are 2 entries
9724  }
9725  }
9726  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime != TDateTime(-1)))
9727  {
9728  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(3, Ptr->ArrivalTime, RepNum, IncMins)) + ": Arrive at " + Ptr->LocationName;
9729  }
9730  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime == TDateTime(-1)))
9731  {
9732  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(4, Ptr->DepartureTime, RepNum, IncMins)) + ": Depart from " + Ptr->LocationName;
9733  }
9734  else if(Ptr->FormatType == PassTime) // new
9735  {
9736  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(5, Ptr->EventTime, RepNum, IncMins)) + ": Pass " + Ptr->LocationName;
9737  }
9738  else if(Ptr->Command == "Fns")
9739  {
9740  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(6, Ptr->EventTime, RepNum, IncMins)) + ": Form new service " +
9741  TrainController->GetRepeatHeadCode(46, Ptr->OtherHeadCode, RepNum, IncDig) + " at " + Ptr->LocationName;
9742  PartStr = ControllerCheckNewServiceDepartureTime(0, Ptr, RepNum, TTDEPtr, Ptr->LinkedTrainEntryPtr, IncMins, PartStr); //if there is a next service this adds the new service departure time to PartStr
9743  }
9744  else if(Ptr->Command == "F-nshs")
9745  {
9746  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(7, Ptr->EventTime, RepNum, IncMins)) + ": Form new service " +
9747  Ptr->NonRepeatingShuttleLinkHeadCode + " at " + Ptr->LocationName;
9748  PartStr = ControllerCheckNewServiceDepartureTime(1, Ptr, 0, TTDEPtr, Ptr->LinkedTrainEntryPtr, IncMins, PartStr); //if there is a next service this adds the new service departure time to RetStr
9749  //note that use LinkedTrainEntryPtr and not NonRepeatingShuttleLinkEntryPtr because the forward link from the feeder is LinkedTrainEntryPtr.
9750  //NonRepeatingShuttleLinkEntryPtr is in the shuttle's ActionVector to point back to the feeder.
9751  //NonRepeatingShuttleLinkEntryPtr is used below from the last shuttle as the forward link to the finishing service
9752  }
9753 //Since this is a new continuation entry service it can't be Fns-sh or Frh-sh but leave these in for consistency with TTrain::FloatingTimetableString
9754  else if((Ptr->Command == "Fns-sh") && (RepNum < (TTDEPtr->NumberOfTrains - 1))) // not the last repeat number
9755  {
9756  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(8, Ptr->EventTime, RepNum, IncMins)) + ": Form new service " +
9757  TrainController->GetRepeatHeadCode(47, Ptr->OtherHeadCode, RepNum + 1, IncDig) + " at " + Ptr->LocationName;
9758  // use RepNum+1 because it's the repeat number of the NEXT shuttle service that is relevant
9759  PartStr = ControllerCheckNewServiceDepartureTime(2, Ptr, RepNum + 1, TTDEPtr, Ptr->LinkedTrainEntryPtr, IncMins, PartStr); //if there is a next service this adds the new service departure time to RetStr
9760  }
9761  else if((Ptr->Command == "Fns-sh") && (RepNum >= (TTDEPtr->NumberOfTrains - 1))) // last repeat number
9762  {
9763  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(9, Ptr->EventTime, RepNum, IncMins)) + ": Form new service " +
9764  Ptr->NonRepeatingShuttleLinkHeadCode, +" at " + Ptr->LocationName;
9765  PartStr = ControllerCheckNewServiceDepartureTime(3, Ptr, 0, TTDEPtr, Ptr->NonRepeatingShuttleLinkEntryPtr, IncMins, PartStr); //if there is a next service this adds the new service departure time to RetStr
9766  }
9767  else if((Ptr->Command == "Frh-sh") && (RepNum < (TTDEPtr->NumberOfTrains - 1))) // not the last repeat number
9768  {
9769  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(10, Ptr->EventTime, RepNum, IncMins)) + ": Form new service " +
9770  TrainController->GetRepeatHeadCode(48, Ptr->OtherHeadCode, RepNum + 1, IncDig) + " at " + Ptr->LocationName;
9771  // use RepNum+1 because it's the repeat number of the NEXT shuttle service that is relevant
9772  PartStr = ControllerCheckNewServiceDepartureTime(4, Ptr, RepNum + 1, TTDEPtr, Ptr->LinkedTrainEntryPtr, IncMins, PartStr); //if there is a next service this adds the new service departure time to RetStr
9773  }
9774  else if((Ptr->Command == "Frh-sh") && (RepNum >= (TTDEPtr->NumberOfTrains - 1))) // last repeat number
9775  {
9776  PartStr = "Terminate at " + Ptr->LocationName;
9777  }
9778  else if(Ptr->Command == "Frh")
9779  {
9780  PartStr = "Terminate at " + Ptr->LocationName;
9781  }
9782  else if(Ptr->Command == "Fer")
9783  {
9784  AnsiString AllowedExits;
9785  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(11, Ptr->EventTime, RepNum, IncMins)) + ": Exit railway" +
9786  TrainController->GetExitLocationAndAt(3, Ptr->ExitList, AllowedExits) + AllowedExits;
9787  }
9788  else if(Ptr->Command == "Fjo")
9789  {
9790  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(12, Ptr->EventTime, RepNum, IncMins)) + ": Join " + TrainController->GetRepeatHeadCode(49,
9791  Ptr->OtherHeadCode, RepNum, IncDig) + " at " + Ptr->LocationName;
9792  }
9793  else if(Ptr->Command == "jbo")
9794  {
9795  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(13, Ptr->EventTime, RepNum, IncMins)) + ": Joined by " + TrainController->GetRepeatHeadCode
9796  (50, Ptr->OtherHeadCode, RepNum, IncDig) + " at " + Ptr->LocationName;
9797  }
9798  else if(Ptr->Command == "fsp")
9799  {
9800  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(14, Ptr->EventTime, RepNum, IncMins)) + ": Front split to " +
9801  TrainController->GetRepeatHeadCode(51, Ptr->OtherHeadCode, RepNum, IncDig) + " at " + Ptr->LocationName;
9802  }
9803  else if(Ptr->Command == "rsp")
9804  {
9805  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(15, Ptr->EventTime, RepNum, IncMins)) + ": Rear split to " +
9806  TrainController->GetRepeatHeadCode(52, Ptr->OtherHeadCode, RepNum, IncDig) + " at " + Ptr->LocationName;
9807  }
9808  else if(Ptr->Command == "cdt")
9809  {
9810  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(16, Ptr->EventTime, RepNum, IncMins)) + ": Change direction at " + Ptr->LocationName;
9811  }
9812  if(RetStr != "")
9813  {
9814  RetStr = RetStr + '\n' + PartStr;
9815  }
9816  else
9817  {
9818  RetStr = PartStr;
9819  }
9820  Count++;
9821  }
9822  while(Ptr < TTDEPtr->ActionVector.end() && (Count < 33) && ((Ptr->Command == "") || ((Ptr->Command != "") && (Ptr->Command[1] != 'F'))));
9823  // limit of 33 allows a max of 34 entries (may have gone from 32 to 34 because of a TimeTimeLoc), which with track and train status gives
9824  // a max of 48 lines, at 13 pixels each, = 624 pixels & screen height has 641 so will fit comfortably. Also 34 timetable entries is as far
9825  // forward as anyone should wish to see without looking at the full timetable
9826  Utilities->CallLogPop(2072);
9827  return(RetStr);
9828 }
9829 
9830 // ---------------------------------------------------------------------------
9831 
9832 AnsiString TTrainController::ControllerCheckNewServiceDepartureTime(int Caller, TActionVectorIterator Ptr, int RptNum, TTrainDataEntry *TDEPtr, TTrainDataEntry *LinkedTrainDataPtr, int IncrementalMinutes, AnsiString RetStr)
9833 {
9834  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + "," + AnsiString(Ptr - &TDEPtr->ActionVector.front()) + ","
9835  + AnsiString(RptNum) + ",ControllerCheckNewServiceDepartureTime," + TDEPtr->HeadCode);
9836  AnsiString DepTime = "", EventTime = "";
9837  bool CDTFlag = false; //reports if train changes direction before departs
9838  TActionVector NewServiceAV = LinkedTrainDataPtr->ActionVector;
9839  for(TActionVectorIterator AVI = NewServiceAV.begin(); AVI < NewServiceAV.end(); AVI++)
9840  {
9841  if(AVI->Command == "cdt")
9842  {
9843  CDTFlag = !CDTFlag; //toggles flag - allows for there being more than one cdt before departure
9844  continue;
9845  }
9846  if((AVI->Command == "fsp") || (AVI->Command == "rsp"))
9847  {
9848  EventTime = Utilities->Format96HHMM(TrainController->GetControllerTrainTime(21, AVI->EventTime, RptNum, IncrementalMinutes));
9849  RetStr += "\nNew service splits at " + EventTime;
9850  Utilities->CallLogPop(2237);
9851  return(RetStr);
9852  }
9853  if(AVI->Command == "jbo")
9854  {
9855  EventTime = Utilities->Format96HHMM(TrainController->GetControllerTrainTime(22, AVI->EventTime, RptNum, IncrementalMinutes));
9856  RetStr += "\nNew service joined by " + AVI->OtherHeadCode + " at " + EventTime;
9857  Utilities->CallLogPop(2238);
9858  return(RetStr);
9859  }
9860  if((AVI->FormatType == TimeLoc) && (AVI->DepartureTime > TDateTime(-1))) //departure time set
9861  {
9862  DepTime = Utilities->Format96HHMM(TrainController->GetControllerTrainTime(23, AVI->DepartureTime, RptNum, IncrementalMinutes));
9863  if(CDTFlag)
9864  {
9865  RetStr += "\nNew service changes direction then departs at " + DepTime;
9866  }
9867  else
9868  {
9869  RetStr += "\nNew service departs at " + DepTime;
9870  }
9871  Utilities->CallLogPop(2239);
9872  return(RetStr);
9873  }
9874  }
9875  Utilities->CallLogPop(2223);
9876  return(RetStr);
9877 }
9878 
9879 // ---------------------------------------------------------------------------
9880 // $$$$$$$$$$$$$$$$$$$$$$ Start of Timetable Functions $$$$$$$$$$$$$$$$$$$$$$$$
9881 /*
9882  Note: The terms 'action' and 'entry' have been used freely for individual code lines within services in comments & in variable names, but
9883  for messages and in the manual and help files the term Entry is reserved for a complete service or train (i.e. an entry in the timetable),
9884  and 'event' is reserved for and individual code line within a service. Repeats use the term 'item' if they use any at all.
9885 
9886  In references to 'HeadCode' can have an optional prefix - up to 4 additional characters that can be anything, so long as last 4 digits
9887  represent the headcode. This allows links to be uniquely identified regardless of the headcode - so can have same headcodes as often as
9888  user wishes
9889 
9890  Prior to start time, anything except a line beginning with a time [...leading spaces...] HH:MM is ignored - can be
9891  descriptive text or anything user wishes
9892  A time on its own line [HH:MM], with or without leading spaces, but with anything following it before the CR (which will
9893  be ignored) is taken as the timetable start time.
9894  Thereafter there must be text on every line in the timetable, as the first blank line (or end of file) will be taken as the end of the
9895  timetable. Text can follow the 'end of timetable' blank line if the user wishes.
9896  A line within the timetable beginning with '*', with or without leading spaces, is ignored. Such lines can add text
9897  within the timetable if required.
9898  Timetable entries consist of one line per headcode (i.e. per service, not necessarily per train, as one train can run several different
9899  services)
9900  Each line starts with HeadCode & full train information for a new train (Snt or Snt-sh), or, for a continuing service
9901  (Sfs, Sns, Sns-sh or Sns-fsh), can have (a) Headcode only or (b) HeadCode + Description, nothing else
9902 
9903  All leading & trailing spaces before & after a line or any entry in a line are stripped off - these can be included to make reading a
9904  text timetable file easier
9905 
9906  form:-
9907  HeadCode[;Description (plain text, no commas or semicolons)][;StartSpeed(kph); MaxRunningSpeed(kph); Mass(tonnes, prog converts to kg);
9908  MaxBrakeRate(tonnes force, prog converts to m/s/s); & gross power(kW, prog converts to power at rail in w)
9909  then multiple entries, separated by commas, of the form:-
9910 
9911  HH:MM;Snt;RearStartIdent FrontStartIdent }StartNew }
9912  HH:MM;Snt-sh;RearStartIdent FrontStartIdent;Fsh HeadCode }SNTShuttle }
9913  HH:MM;Sns-sh;Fxx-sh HeadCode;F-nshs HeadCode (non-repeating)}SNSShuttle }
9914 
9915  HH:MM;Command;HeadCode (Sfs Sns jbo fsp rsp Fns Fjo Frh-sh) }TimeCmdHeadCode } Train action entries
9916  HH:MM;F-nshs;NonRepeatingShuttleLinkHeadCode }FNSNonRepeatToShuttle }
9917  HH:MM;Sns-fsh;NonRepeatingShuttleLinkHeadCode }SNSNonRepeatFromShuttle }
9918 
9919  HH:MM;Command (cdt) }TimeCmd }
9920  HH:MM;Location (arr & dep) }TimeLoc }
9921  HH:MM;HH:MM;Location }TimeTimeLoc }
9922  HH:MM;pas;Location }PassTime }
9923  HH:MM;Fns-sh;Snx-sh HeadCode;Sns-fsh HeadCode (non-rep) }FSHNewService }
9924  HH:MM;Fer;set of allowable IDs }ExitRailway }
9925  Command (Frh only) }FinRemHere }
9926 
9927  R;mm;dd;nn. Repeat Repeat entry
9928 
9929  Formats:
9930 
9931  Command only: Frh
9932  Time;Command: cdt
9933  Time;Command;Headcode: Sfs Sns jbo fsp rsp Fns Fjo Frh-sh F-nshs Sns-fsh
9934  Time;Command;2 Element IDs: Snt
9935  Time;Comand;n Element IDs: Fer
9936  Time;Command;rep Headcode;nonrep Headcode: Sns-sh Fns-sh
9937  Time;Command;2 Element IDs;Headcode Snt-sh
9938  Time;Command;Location pas
9939  Time;Location Arr Dep
9940  Time;Time;Location Arr & dep together
9941 
9942  9 Single entries: Snt (located or unlocated); pas; cdt; TimeLoc arr & dep; TimeTimeLoc; Fer; Frh
9943 
9944  9 1x Linked entries: Non-shuttle: fsp or rsp -> Sfs; Fns -> Sns; Fjo -> jbo; times must match, headcodes must match
9945  Shuttle: F-nshs -> Sns-sh: times match, F-nshs HeadCode matches Sns-sh 2nd Headcode;
9946  Fns-sh -> Sns-fsh: Fns-sh time + all repeats = Sns-fsh time, Fns-sh 2nd headcode matches Sns-fsh Headcode
9947 
9948  4 2x Linked entries, all shuttles:
9949 
9950  Frh-sh -> Snt-sh: Frh-sh time = Snt-sh time + 1 repeat while repeating, Frh-sh Headcode = Snt-sh Headcode;
9951  -> Sns-sh: Frh-sh time = Sns-sh time + 1 repeat while repeating, Frh-sh Headcode = Sns-sh 1st Headcode;
9952  -> Remain Here (at finish location after all repeats)
9953  Fns-sh -> Snt-sh: Frh-sh time = Snt-sh time + 1 repeat while repeating, Fns-sh 1st Headcode = Snt-sh Headcode
9954  -> Sns-sh: Frh-sh time = Sns-sh time + 1 repeat while repeating, Fns-sh 1st Headcode = Sns-sh 1st Headcode
9955 
9956  Allowable successors:-
9957 
9958  Successor state Type
9959 
9960  Snt located AtLoc ) Snt AtLoc successors: TimeLoc dep/jbo/fsp/rsp/cdt/Frh/Fns/Fjo/Frh-sh/Fns-sh/F-nshs;
9961  Snt Unlocated Moving ) Snt Moving successors: TimeLoc arr/TimeTimeLoc/pas/Fer;
9962  Sfs AtLoc )
9963  Sns AtLoc ) Start
9964  Sns-fsh AtLoc )
9965  Snt-sh AtLoc )
9966  Sns-sh AtLoc )
9967 
9968  pas Moving )
9969  jbo AtLoc )
9970  fsp AtLoc )
9971  rsp AtLoc ) Intermediate
9972  cdt AtLoc )
9973  TimeLoc arr Moving (bef), AtLoc (aft) )
9974  TimeLoc dep AtLoc (bef), Moving (aft) )
9975  TimeTimeLoc Moving )
9976 
9977  Fns Repeat/Nothing)
9978  Fjo Repeat/Nothing)
9979  Frh Repeat/Nothing)
9980  Fer Repeat/Nothing) Finish
9981  Frh-sh Repeat )
9982  Fns-sh Repeat )
9983  F-nshs Nothing )
9984 
9985  Descriptions:
9986  Snt New train
9987  Sfs New service from split
9988  Sns New service from another service
9989  Sns-fsh New non-repeating service from a shuttle service
9990  Snt-sh New shuttle train at a timetabled stop
9991  Sns-sh New shuttle service from a feeder service
9992 
9993  pas Pass
9994  jbo Be joined by another train
9995  fsp Front split
9996  rsp Rear split
9997  cdt Change direction of train
9998  TimeLoc arr Arrival
9999  TimeLoc dep Departure
10000  TimeTimeLoc Arrival and departure
10001 
10002  Fns Finish & form a new service
10003  Fjo Finish & join another train
10004  Frh Finish & remain here
10005  Fer Finish & exit railway
10006  Frh-sh Finish & repeat shuttle, finally remain here
10007  Fns-sh Finish & repeat shuttle, finally form a non-repeating service
10008  F-nshs Finish & form a shuttle feeder service
10009 */
10010 
10011 bool TTrainController::TimetableIntegrityCheck(int Caller, char *FileName, bool GiveMessages, bool CheckLocationsExistInRailway) // true for success
10012 {
10013  // Error messages mainly given in called functions, five are given here - empty file; inability to find a start time; timetable containing
10014  // a line that is too long; timetable containing too few lines; and timetable failed to open.
10015  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TimetableIntegrityCheck," + AnsiString(FileName));
10016  // new for v0.2b
10017  // compile ActiveTrackElementNameMap
10018  TTrack::TActiveTrackElementNameMapEntry ActiveTrackElementNameMapEntry;
10019 
10021  for(unsigned int x = 0; x < Track->TrackVector.size(); x++)
10022  {
10023  // if((Track->TrackElementAt(, x).ActiveTrackElementName != "") && (Track->TrackElementAt(, x).TrackType != Continuation))
10025  == Track->ContinuationNameMap.end())
10026  {
10027  // exclude any name that appears in a continuation, error message given in tt validation if try to include such a name in a tt
10028  ActiveTrackElementNameMapEntry.first = Track->TrackElementAt(1035, x).ActiveTrackElementName;
10029  ActiveTrackElementNameMapEntry.second = 0; // this is a dummy value
10030  Track->ActiveTrackElementNameMap.insert(ActiveTrackElementNameMapEntry);
10031  }
10032  }
10034  // end of new section
10035  std::ifstream TTBLFile(FileName, std::ios_base::binary);
10036 
10037  // binary mode so the "\r\n" pairs stay as they are rather than being entered as '\n'
10038  if(TTBLFile.is_open())
10039  {
10040  char *TrainTimetableString = new char[10000];
10041  // enough for over 200 stations, should be adequate!
10042  bool EndOfFile = false;
10043  int Count = 0;
10044  // counts 'relevant' lines, i.e ignores any before the start time on its own line
10045  TTBLFile.getline(TrainTimetableString, 10000, '\0');
10046  // delimiter is '\0' as it's an AnsiString
10047  if(TTBLFile.eof() && (TrainTimetableString[0] == '\0'))
10048  // file empty - stores a null in 1st position if doesn't load any characters
10049  {
10050  // may still have eof even if read a line (no CRLF at end), and
10051  // if so need to process it
10052  TimetableMessage(GiveMessages, "Timetable invalid - file empty");
10053  TTBLFile.close();
10054  delete[] TrainTimetableString;
10055  Utilities->CallLogPop(1611);
10056  return(false);
10057  }
10058  AnsiString OneLine(TrainTimetableString);
10059  bool FinalCallFalse = false;
10060  while((Count == 0) && !ProcessOneTimetableLine(5, Count, OneLine, EndOfFile, FinalCallFalse, GiveMessages, CheckLocationsExistInRailway))
10061  // get rid of lines before the start time
10062  {
10063  // ProcessOneTimetableLine returns true for a valid start time, an EndOfFile &/or a blank entry
10064  TTBLFile.getline(TrainTimetableString, 10000, '\0');
10065  if(TTBLFile.eof() && (TrainTimetableString[0] == '\0'))
10066  // stores a null in 1st position if doesn't load any characters
10067  {
10068  // may still have eof even if read a line (no CRLF at end), and
10069  // if so need to process it
10070  TimetableMessage(GiveMessages, "Timetable invalid - unable to find a valid start time on its own line");
10071  TTBLFile.close();
10072  delete[] TrainTimetableString;
10073  Utilities->CallLogPop(772);
10074  return(false);
10075  }
10076  OneLine = AnsiString(TrainTimetableString);
10077  }
10078  // here when have accepted the start time
10079  Count++; // increment past the start time
10080  while(!EndOfFile)
10081  {
10082  TTBLFile.getline(TrainTimetableString, 10000, '\0');
10083  // get next line after start time
10084  if(TTBLFile.eof() && (TrainTimetableString[0] == '\0'))
10085  // stores a null in 1st position if doesn't load any characters
10086  {
10087  // may still have eof even if read a line (no CRLF at end), and
10088  // if so need to process it
10089  EndOfFile = true;
10090  OneLine = "";
10091  }
10092  else
10093  {
10094  OneLine = AnsiString(TrainTimetableString);
10095  }
10096  if(OneLine.Length() > 9999)
10097  {
10098  TimetableMessage(GiveMessages, "Timetable contains a line that is too long - 10,000 or more characters!");
10099  TTBLFile.close();
10100  delete[] TrainTimetableString;
10101  Utilities->CallLogPop(789);
10102  return(false);
10103  }
10104  bool FinalCallFalse = false;
10105  if(!ProcessOneTimetableLine(6, Count, OneLine, EndOfFile, FinalCallFalse, GiveMessages, CheckLocationsExistInRailway))
10106  // false for FinalCall - just checking at this stage
10107  {
10108  TTBLFile.close();
10109  delete[] TrainTimetableString;
10110  Utilities->CallLogPop(770);
10111  return(false);
10112  }
10113  if(EndOfFile && (Count < 2))
10114  // Timetable must contain at least two relevant lines, one for start time and at least one train
10115  {
10116  TimetableMessage(GiveMessages, "Timetable has too few or no relevant entries - must have a start time on its own line and at least one train");
10117  TTBLFile.close();
10118  delete[] TrainTimetableString;
10119  Utilities->CallLogPop(771);
10120  return(false);
10121  }
10122  Count++;
10123  }
10124  delete[] TrainTimetableString;
10125  TTBLFile.close();
10126  } // if(TTBLFile.is_open())
10127  else
10128  {
10129  TimetableMessage(GiveMessages, "Failed to open timetable file, make sure it's spelled correctly, it exists and isn't open in another application");
10130  Utilities->CallLogPop(2154);
10131  return(false);
10132  }
10133  Utilities->CallLogPop(753);
10134  return(true);
10135 }
10136 
10137 // ---------------------------------------------------------------------------
10138 
10139 bool TTrainController::ProcessOneTimetableLine(int Caller, int Count, AnsiString OneLine, bool &EndOfFile, bool FinalCall, bool GiveMessages,
10140  bool CheckLocationsExistInRailway) // return true for success
10141 
10142 /* Format:
10143  Prior to start time, anything except a line beginning with a time [...leading spaces...] HH:MM is ignored - can be
10144  descriptive text or anything user wishes
10145  A time on its own line [HH:MM], with or without leading spaces, but with anything following it before the CR (which will
10146  be ignored) is taken as the timetable start time.
10147  Thereafter there must be text on every line in the timetable, as the first blank line (or end of file) will be taken as the end of the
10148  timetable. Text can follow the 'end of timetable' blank line if the user wishes.
10149  A line within the timetable beginning with '*', with or without leading spaces, is ignored. Such lines can add text
10150  within the timetable if required.
10151  Timetable entries consist of one line per headcode (i.e. per service, not necessarily per train, as one train can run several different
10152  services)
10153  Each line starts with HeadCode & full train information for a new train (Snt or Snt-sh), or, for a continuing service
10154  (Sfs, Sns, Sns-sh or Sns-fsh), can have (a) Headcode only or (b) HeadCode + Description, nothing else
10155 
10156  All leading & trailing spaces before & after a line or any entry in a line are stripped off - these can be included to make reading a
10157  text timetable file easier
10158 
10159  form:-
10160  HeadCode[;Description (plain text, no commas or semicolons)][;StartSpeed(kph); MaxRunningSpeed(kph); Mass(tonnes, prog converts to kg);
10161  MaxBrakeRate(tonnes force, prog converts to m/s/s); & gross power(kW, prog converts to power at rail in w)
10162  then multiple entries, separated by commas, of the form:-
10163 
10164  Format FormatType
10165  [W]HH:MM;Command (cdt) }TimeCmd }
10166  [W]HH:MM;Fer;set of allowable IDs }ExitRailway }
10167  [W]HH:MM;pas;Location }PassTime }
10168  [W]HH:MM;Snt;RearStartIdent FrontStartIdent }StartNew }
10169  [W]HH:MM;Command;HeadCode (Sfs Sns jbo fsp rsp Fns Fjo Frh-sh) }TimeCmdHeadCode }
10170  [W]HH:MM;F-nshs;non-repeating headcode }FNSNonRepeatToShuttle }
10171  [W]HH:MM;Sns-fsh;NonRepeatingShuttleLinkHeadCode }SNSNonRepeatFromShuttle } Train action entries
10172  [W]HH:MM;Snt-sh;RearStartIdent FrontStartIdent;FSH HeadCode }SNTShuttle }
10173  [W]HH:MM;Sns-sh;FSH HeadCode;F-nshs HeadCode (non-repeating) }SNSShuttle }
10174  [W]HH:MM;Fns-sh;Details }FSHNewService }
10175  [W]HH:MM;Location (arr & dep) }TimeLoc }
10176  [W]HH:MM;HH:MM;Location }TimeTimeLoc }
10177  Command (Frh only) }FinRemHere }
10178 
10179  R;mm;dd;nn. Repeat Repeat entry
10180 
10181  Two times represent arrival & departure, without any other events between (if arrival and departure times are the same
10182  then departure is 30 sec after arrival), single time represents (a) event time; (b) arrival time if train not already
10183  at location; or (c) departure time if train already at location (including train started at location either as a new
10184  train or as a continuation service train at that location). All lines must contain a start entry and a finish entry,
10185  the finish being the last unless there is a repeat entry. The repeat entry begins with 'R', then the incremental
10186  minutes, incremental train headcode last 2 digits, and number of repeats.
10187 
10188  Shuttle entries are where can loop back to an earlier Snt-sh or Sns-sh entry from a Frh-sh or Fns-sh (Finish Shuttle)
10189  entry. Here the shuttle start can have two entries, one from a set position (Snt-sh, must be located) or from a F-nshs
10190  (Sns-sh) - with NO repeat from this source, and from a Fxx-sh, with repeats. After all shuttle repeats Frh-sh remains
10191  where it is, and Fns-sh links to a new service (via an Sns entry), but there must be no repeats in this new service
10192  (it's for a shuttle train to return to depot at end of services)
10193 
10194  Command/Location & details are as follows:-
10195 
10196  Although headcodes can be duplicated, all joins, splits, new services etc give other headcode from both trains' povs, and
10197  these have to match once only, i.e. if 2E44 splits to 2E45 then it can't split to 2E45 anywhere else, and 2E45 must give
10198  2E44 in its Sfs entry. All these are checked.
10199  ***add note re shuttles & their use of otherheadcodes + non-repeating headcodes***
10200 
10201  Start commands:-
10202  Snt (StartNew) = Start New Train, i.e. create new train, details = rearstartident, space, frontstartident (can't confuse
10203  with loc as a start entry can't have a location as details)
10204  Sfs (TimeCmdHeadCode) = Start From Split, create a new train that has split from another train (& listed in other train's
10205  timetable line), details = other headcode - (can't confuse with loc as start can't be a loc)
10206  Sns (TimeCmdHeadCode) = Start, headcode change from earlier service - no need to create train as already exists, it just
10207  changes its relevant information, details = old headcode (can't confuse with loc as start can't be a loc)
10208  Snt-sh (SNTShuttle) = Start New Train, i.e. create new train, details = rearstartident, space, frontstartident (can't
10209  confuse with loc as start can't be a loc) then the Fsh-XX service headcode (OtherHeadCode can't be same headcode)
10210  Sns-sh (SNSShuttle) = Start, headcode change from earlier service - no need to create train as already exists, it just
10211  changes its relevant information, details = the FSH-XX service headcode (OtherHeadCode, can't be same headcode)
10212  followed by the non-repeating F-nshs headcode (NonRepeatingShuttleLinkHeadCode)
10213  Sns-fsh (SNSNonRepeatFromShuttle) = Start as a non-repeating service from a shuttle service that has finished all its
10214  repeats, details = NonRepeatingShuttleLinkHeadCode for the corresponding shuttle Fns-sh service
10215 
10216  Intermediate commands:-
10217  Time - Location (TimeLoc), can be arrival or departure depending on context
10218  Time Time location (TimeTimeLoc), arrival and departure
10219  Location Name (exactly as used in the railway) in TimeLoc & TimeTimeLoc means that the train is required to stop at the location
10220  pas (PassTime), Time;pas;Location
10221  jbo (TimeCmdHeadCode) = Joined By Other = joined by other train, details = new headcode (await other train - may be delayed). Note that the
10222  joining train's finish details must correspond or the file check will fail
10223  fsp (TimeCmdHeadCode) = Front Split = a new train splits away from front of this train, both trains in same direction, details = new headcode (create
10224  new train - that train's starting information must correspond)
10225  rsp (TimeCmdHeadCode) = Rear Split = a new train splits away from rear of this train, both trains in same direction, details = new headcode (create
10226  new train - that train's starting information must correspond)
10227  cdt (TimeCmd) = Change Direction of Train = change direction, no details needed & no train creation
10228 
10229  Finish commands:-
10230  Fns (TimeCmdHeadCode) = Finish New Service = finish, form new service in same direction, details = new headcode (no train
10231  creation)
10232  F-nshs (FNSShuttle) = Finish New Service (Shuttle) = finish, form new shuttle service in same direction, details =
10233  shuttle headcode (no train creation)
10234  Fjo (TimeCmdHeadCode) = Finish Join Other = finish, join other train (which must be on an adjacent element, either end -
10235  may have to wait for it), details = new headcode (delete train)
10236  Frh (FinRemHere) = Finish Remain Here = stay here indefinitely, no details & no time needed
10237  Fer (ExitRailway) = Finish, exit railway (i.e at a continuation) - details = set of allowable exit IDs
10238  Frh-sh (TimeCmdHeadCode) = Finish then restart as a shuttle using Snt-sh or Sns-sh, when all shuttle repeats done remain
10239  here
10240  Fns-sh (FSHNewService) = Finish then restart as a shuttle using Snt-sh or Sns-sh, when all shuttle repeats done form new
10241  service via Sns-fsh using the NonRepeatingShuttleLinkHeadCode
10242 
10243  Repeat:-
10244  R;mm;dd;nn (Repeat) where mm = minute increment, dd = 2nd 2 headcode digit increment & nn = no. of repeats (no max as can duplicate
10245  headcodes - it is up to user to avoid duplicates if he/she wishes to.
10246 
10247  Checks carried out with error messages in this function:-
10248  At least one comma in a service line (it's based on a .csv file)
10249  No entries following train information;
10250  At least one comma in remainder after train information (i.e at least a start and a finish entry);
10251  SplitEntry returns false in an intermediate entry - message repeats the entry for information;
10252  First entry not a start entry;
10253  Train information incomplete before a start entry;
10254  Entry follows a finish entry but doesn't begin with 'R';
10255  SplitEntry returns false in a finish entry - message repeats the entry for information;
10256  Last action entry isn't a finish entry.
10257 
10258  Function returns false with no message if:-
10259  Timetable start time invalid (no message is given for an invalid time as the line is assumed to be an irrelevant line; if no start
10260  time is found at all then an error message is given in the calling function);
10261  SplitTrainInfo returns false (message given in called function);
10262  SplitRepeat returns false (message given in called function).
10263 */{
10264  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ProcessOneTimetableLine," + AnsiString(Count) + "," + OneLine + "," +
10265  AnsiString((short)FinalCall) + "," + AnsiString((short)CheckLocationsExistInRailway));
10266  TTrainDataEntry TempTrainDataEntry;
10267 
10268  EndOfFile = false;
10269  StripSpaces(0, OneLine);
10270  // strip both leading and trailing spaces at ends of line and spaces before and after all commas and
10271  // semicolons within the line
10272  ServiceReference = "";
10273  if(OneLine != "")
10274  {
10275  if(OneLine[1] != '*')
10276  {
10277  int SCPos = OneLine.Pos(';');
10278  if(SCPos == 0)
10279  {
10280  ServiceReference = OneLine.SubString(1, 8);
10281  }
10282  else
10283  {
10284  ServiceReference = OneLine.SubString(1, (SCPos - 1));
10285  }
10286  }
10287  }
10288  bool AllCommas = true;
10289 
10290  for(int x = 1; x < OneLine.Length() + 1; x++) // check for nothing but commas (may be all commas if created from Excel) or a blank line
10291  {
10292  if(OneLine[x] != ',')
10293  {
10294  AllCommas = false;
10295  }
10296  }
10297  if(AllCommas || (OneLine == ""))
10298  {
10299  if(Count > 0)
10300  {
10301  EndOfFile = true;
10302  // returns true for a blank line - treated as end of file
10303  Utilities->CallLogPop(1018);
10304  return(true);
10305  }
10306  else // count == 0 so not yet found a start time, no message to be given
10307  {
10308  Utilities->CallLogPop(754);
10309  return(false);
10310  }
10311  }
10312  AnsiString First = "", Second = "", Third = "", Fourth = "";
10313  int RearStartOrRepeatMins = 0, FrontStartOrRepeatDigits = 0, NumberOfRepeats = 0;
10314  TDateTime EventTime(0), ArrivalTime(0), DepartureTime(0);
10315  TDateTime StartTime(0);
10316  TNumList ExitList;
10317  bool Warning = false;
10318 
10319  if(Count == 0) // no start time found yet
10320  {
10321 /* dropped at v0.6b
10322  AnyHeadCodeValid = false;
10323  if(OneLine.SubString(6,5) == ";0000")
10324  {
10325  AnyHeadCodeValid = true;
10326  }
10327 */
10328  if(!CheckTimeValidity(0, OneLine, StartTime))
10329  {
10330  // no message is given for an invalid time as it's assumed to be an irrelevant line
10331  // if no start time is found at all then an error message is given in the calling function
10332  // AnyHeadCodeValid = false;
10333  Utilities->CallLogPop(755);
10334  return(false);
10335  }
10336  if(FinalCall) // here if start time valid
10337  {
10338  TTClockTime = StartTime;
10339  TimetableStartTime = StartTime;
10340  }
10341  }
10342  else
10343  {
10344  AnsiString TrainInfoStr = "", HeadCode = "", Description = "";
10345  int StartSpeed = 0, MaxRunningSpeed = 0, Mass = 0;
10346  double MaxBrakeRate = 0;
10347  double PowerAtRail = 0;
10348  int SignallerSpeed = 0;
10349  if(OneLine[1] == '*')
10350  {
10351  Utilities->CallLogPop(1581);
10352  return(true);
10353  // ignore any line beginning with '*' but return true as there is no error
10354  }
10355  int Pos = OneLine.Pos(',');
10356  if(Pos == 0)
10357  {
10358  int SubStringLength = 20;
10359  if(OneLine.Length() < 20)
10360  {
10361  SubStringLength = OneLine.Length();
10362  }
10363  TimetableMessage(GiveMessages, "Error in timetable - entry incomplete: see '" + OneLine.SubString(1, SubStringLength) + "'....");
10364  Utilities->CallLogPop(766);
10365  return(false);
10366  }
10367  TrainInfoStr = OneLine.SubString(1, Pos - 1);
10368  if(!SplitTrainInfo(0, TrainInfoStr, HeadCode, Description, StartSpeed, MaxRunningSpeed, Mass, MaxBrakeRate, PowerAtRail, SignallerSpeed,
10369  GiveMessages)) // error messages given in SplitTrainInfo
10370  {
10371  Utilities->CallLogPop(773);
10372  return(false);
10373  }
10374  if(FinalCall)
10375  {
10376  // store Train info - conversions done in SplitTrainInfo
10377  // only headcode mandatory for continuing services
10378  TempTrainDataEntry.HeadCode = HeadCode;
10379  TempTrainDataEntry.ServiceReference = HeadCode;
10380  TempTrainDataEntry.Description = Description;
10381  TempTrainDataEntry.StartSpeed = StartSpeed;
10382  TempTrainDataEntry.Mass = Mass;
10383  TempTrainDataEntry.MaxRunningSpeed = MaxRunningSpeed;
10384  TempTrainDataEntry.MaxBrakeRate = MaxBrakeRate;
10385  TempTrainDataEntry.PowerAtRail = PowerAtRail;
10386  TempTrainDataEntry.SignallerSpeed = SignallerSpeed;
10387  TTrainOperatingData TempTrainOperatingData;
10388  TempTrainDataEntry.TrainOperatingDataVector.push_back(TempTrainOperatingData); // push empty vector for now
10389  }
10390  AnsiString NewRemainder = OneLine.SubString(Pos + 1, OneLine.Length() - Pos);
10391  // now left with series of entries for this train, but there may be a string of commas at the end of the line if created by Excel
10392  // so strip them off
10393  while(NewRemainder[NewRemainder.Length()] == ',')
10394  {
10395  if(NewRemainder.Length() > 1)
10396  {
10397  NewRemainder = NewRemainder.SubString(1, NewRemainder.Length() - 1);
10398  }
10399  else
10400  {
10401  NewRemainder = "";
10402  break;
10403  }
10404  }
10405  // check if zero length & fail if so
10406  if(NewRemainder == "")
10407  {
10408  TimetableMessage(GiveMessages, "Error in timetable - no events following train: '" + OneLine + "'");
10409  Utilities->CallLogPop(769);
10410  return(false);
10411  }
10412  // now have one more entry than there are commas
10413  int CommaCount = 0;
10414  for(int x = 1; x < NewRemainder.Length() + 1; x++)
10415  {
10416  if(NewRemainder[x] == ',')
10417  {
10418  CommaCount++;
10419  }
10420  } // must have at least 1 comma, for start & finish entries, unless train is entered under signaller control
10421  if(CommaCount == 0)
10422  {
10423  if((NewRemainder.SubString(7, 3) != "Snt") || (NewRemainder[NewRemainder.Length()] != 'S'))
10424  {
10425  int SubStringLength = 20;
10426  if(OneLine.Length() < 20)
10427  {
10428  SubStringLength = OneLine.Length();
10429  }
10430  TimetableMessage(GiveMessages,
10431  "Error in timetable - must have at least a start and a finish event for a train that is not started under signaller control - see line beginning: '" +
10432  OneLine.SubString(1, SubStringLength) + "'....");
10433  Utilities->CallLogPop(783);
10434  return(false);
10435  }
10436  }
10437  AnsiString OneEntry = "";
10438  TTimetableFormatType FormatType;
10439  TTimetableSequenceType SequenceType;
10440  TTimetableLocationType LocationType;
10441  TTimetableShuttleLinkType ShuttleLinkType;
10442  bool FinishFlag = false;
10443  for(int x = 0; x < CommaCount + 1; x++)
10444  {
10445  if((CommaCount == 0) || (x < CommaCount))
10446  // i.e. train entered under signaller control with no repeats, or entry is not the last,
10447  // in which case there's a comma & finish element or repeat still to come this entry could
10448  // be a finish but can't be a repeat
10449  {
10450  if(CommaCount == 0)
10451  {
10452  OneEntry = NewRemainder;
10453  NewRemainder = "";
10454  }
10455  else
10456  {
10457  Pos = NewRemainder.Pos(',');
10458  OneEntry = NewRemainder.SubString(1, Pos - 1);
10459  NewRemainder = NewRemainder.SubString(Pos + 1, NewRemainder.Length() - Pos);
10460  }
10461  First = "";
10462  Second = "";
10463  Third = "";
10464  Fourth = "";
10465  RearStartOrRepeatMins = 0;
10466  FrontStartOrRepeatDigits = 0;
10467  NumberOfRepeats = 0;
10468  if(!SplitEntry(0, OneEntry, GiveMessages, CheckLocationsExistInRailway, First, Second, Third, Fourth, RearStartOrRepeatMins,
10469  FrontStartOrRepeatDigits, FormatType, LocationType, SequenceType, ShuttleLinkType, ExitList, Warning))
10470  {
10471  TimetableMessage(GiveMessages, "Error in timetable - Event: '" + OneEntry + "'");
10472  Utilities->CallLogPop(756);
10473  return(false);
10474  }
10475  // check if warning for Frh or Fjo & reject
10476  if(Warning && (Second == "Frh"))
10477  {
10478  TimetableMessage(GiveMessages, "Error in line - '" + OneEntry + "': warnings cannot be given for 'Frh' events");
10479  Utilities->CallLogPop(1793);
10480  return(false);
10481  }
10482  if(Warning && (Second == "Fjo"))
10483  {
10484  TimetableMessage(GiveMessages, "Error in line - '" + OneEntry +
10485  "': warnings cannot be given for 'Fjo' events, for a train join warning add a 'W' prefix to the 'jbo' event");
10486  Utilities->CallLogPop(1794);
10487  return(false);
10488  }
10489  if(x == 0) // should be start event
10490  {
10491  if(SequenceType != Start)
10492  {
10493  TimetableMessage(GiveMessages, "Error in timetable - First event not a start event: '" + OneEntry + "'");
10494  Utilities->CallLogPop(784);
10495  return(false);
10496  }
10497  if((Second == "Snt") && (Fourth == 'S') && (NewRemainder != ""))
10498  {
10499  if(NewRemainder[1] != 'R')
10500  {
10501  TimetableMessage(GiveMessages,
10502  "Error in timetable - the only event that can follow a train created under signaller control is a repeat, see '" +
10503  OneEntry + "'");
10504  Utilities->CallLogPop(787);
10505  return(false);
10506  }
10507  }
10508  if((Second == "Snt") || (Second == "Snt-sh"))
10509  // need full train information including non-default values for at least HeadCode, Description,
10510  // MaxRunningSpeed, Mass, MaxBrakeRate, & PowerAtRail
10511  {
10512  if((HeadCode == "") || (Description == "") || (MaxRunningSpeed == 0) || (Mass == 0) || (MaxBrakeRate == 0)) // ||
10513  // (PowerAtRail == 0)) allowed 0 for power at v2.4.0
10514  {
10515  TimetableMessage(GiveMessages, "Error in timetable - train information incomplete before 'Snt' or 'Snt-sh' start event: '" +
10516  OneEntry + "'");
10517  Utilities->CallLogPop(1783);
10518  return(false);
10519  }
10520  }
10521  if((Second == "Sfs") || (Second == "Sns") || (Second == "Sns-sh") || (Second == "Sns-fsh"))
10522  // service continuation - need at least non-default value for HeadCode
10523  {
10524  if(HeadCode == "")
10525  {
10526  TimetableMessage(GiveMessages, "Error in timetable - headcode missing before 'Sfs', 'Sns', 'Sns-sh' or 'Sns-fsh' start event: '" +
10527  OneEntry + "'");
10528  Utilities->CallLogPop(788);
10529  return(false);
10530  }
10531  if((StartSpeed != 0) || (MaxRunningSpeed != 0) || (Mass != 0) || (MaxBrakeRate != 0) || (PowerAtRail != 0))
10532  {
10533  TimetableMessage(GiveMessages,
10534  "Error in timetable - information additional to a headcode & optional description given before 'Sfs', 'Sns', 'Sns-sh' or 'Sns-fsh' start event: '" +
10535  OneEntry + "'");
10536  Utilities->CallLogPop(843);
10537  return(false);
10538  }
10539  }
10540  }
10541  if(SequenceType == Finish)
10542  {
10543  FinishFlag = true;
10544  // marker for only permitted additional entry being a repeat, only needed if the
10545  // finish entry is not the last entry
10546  }
10547  if(FinalCall)
10548  {
10549  // interpret & add to ActionVector
10550  TDateTime TempTime;
10551  TActionVectorEntry ActionVectorEntry;
10552  ActionVectorEntry.FormatType = FormatType;
10553  ActionVectorEntry.LocationType = LocationType;
10554  ActionVectorEntry.SequenceType = SequenceType;
10555  ActionVectorEntry.ShuttleLinkType = ShuttleLinkType;
10556  ActionVectorEntry.Warning = Warning;
10557  if(FormatType == TimeLoc)
10558  {
10559  if(CheckTimeValidity(1, First, ActionVectorEntry.EventTime))
10560  {
10561  ;
10562  } // these will all be true as final call
10563 
10564  ActionVectorEntry.LocationName = Second;
10565  }
10566  else if(FormatType == PassTime) // new
10567  {
10568  if(CheckTimeValidity(17, First, ActionVectorEntry.EventTime))
10569  {
10570  ;
10571  }
10572  ActionVectorEntry.Command = Second;
10573  ActionVectorEntry.LocationName = Third;
10574  }
10575  else if(FormatType == TimeTimeLoc)
10576  {
10577  if(CheckTimeValidity(2, First, ActionVectorEntry.ArrivalTime))
10578  {
10579  ;
10580  }
10581  if(CheckTimeValidity(3, Second, ActionVectorEntry.DepartureTime))
10582  {
10583  ;
10584  }
10585  ActionVectorEntry.LocationName = Third;
10586  }
10587  else if(FormatType == TimeCmd)
10588  {
10589  if(CheckTimeValidity(4, First, ActionVectorEntry.EventTime))
10590  {
10591  ;
10592  }
10593  ActionVectorEntry.Command = Second;
10594  }
10595  else if(FormatType == ExitRailway)
10596  {
10597  if(CheckTimeValidity(18, First, ActionVectorEntry.EventTime))
10598  {
10599  ;
10600  }
10601  ActionVectorEntry.Command = Second;
10602  ActionVectorEntry.ExitList = ExitList;
10603  }
10604  else if(FormatType == StartNew)
10605  {
10606  if(CheckTimeValidity(5, First, ActionVectorEntry.EventTime))
10607  {
10608  ;
10609  }
10610  ActionVectorEntry.Command = Second;
10611  ActionVectorEntry.RearStartOrRepeatMins = RearStartOrRepeatMins;
10612  ActionVectorEntry.FrontStartOrRepeatDigits = FrontStartOrRepeatDigits;
10613  if(Fourth == 'S')
10614  {
10615  ActionVectorEntry.SignallerControl = true;
10616  }
10617  }
10618  else if(FormatType == SNTShuttle)
10619  {
10620  if(CheckTimeValidity(6, First, ActionVectorEntry.EventTime))
10621  {
10622  ;
10623  }
10624  ActionVectorEntry.Command = Second;
10625  ActionVectorEntry.RearStartOrRepeatMins = RearStartOrRepeatMins;
10626  ActionVectorEntry.FrontStartOrRepeatDigits = FrontStartOrRepeatDigits;
10627  ActionVectorEntry.OtherHeadCode = Fourth;
10628  }
10629  else if(FormatType == SNSShuttle)
10630  {
10631  if(CheckTimeValidity(7, First, ActionVectorEntry.EventTime))
10632  {
10633  ;
10634  }
10635  ActionVectorEntry.Command = Second;
10636  ActionVectorEntry.OtherHeadCode = Third;
10637  ActionVectorEntry.NonRepeatingShuttleLinkHeadCode = Fourth;
10638  }
10639  else if(FormatType == TimeCmdHeadCode)
10640  {
10641  if(CheckTimeValidity(8, First, ActionVectorEntry.EventTime))
10642  {
10643  ;
10644  }
10645  ActionVectorEntry.Command = Second;
10646  ActionVectorEntry.OtherHeadCode = Third;
10647  }
10648  else if((FormatType == FNSNonRepeatToShuttle) || (FormatType == SNSNonRepeatFromShuttle))
10649  {
10650  if(CheckTimeValidity(9, First, ActionVectorEntry.EventTime))
10651  {
10652  ;
10653  }
10654  ActionVectorEntry.Command = Second;
10655  ActionVectorEntry.NonRepeatingShuttleLinkHeadCode = Third;
10656  }
10657  else if(FormatType == FSHNewService)
10658  {
10659  if(CheckTimeValidity(10, First, ActionVectorEntry.EventTime))
10660  {
10661  ;
10662  }
10663  ActionVectorEntry.Command = Second;
10664  ActionVectorEntry.OtherHeadCode = Third;
10665  ActionVectorEntry.NonRepeatingShuttleLinkHeadCode = Fourth;
10666  }
10667  else if(FormatType == FinRemHere)
10668  {
10669  ActionVectorEntry.Command = Second;
10670  }
10671  TempTrainDataEntry.ActionVector.push_back(ActionVectorEntry);
10672  }
10673  }
10674  else // last entry, & not entered under signaller control with no repeats, i.e. could be finish or repeat
10675  {
10676  OneEntry = NewRemainder;
10677  First = "";
10678  Second = "";
10679  Third = "";
10680  Fourth = "";
10681  RearStartOrRepeatMins = 0;
10682  FrontStartOrRepeatDigits = 0;
10683  NumberOfRepeats = 0;
10684  if((FinishFlag) && (OneEntry[1] != 'R'))
10685  // already had a finish entry
10686  {
10687  TimetableMessage(GiveMessages, "Error in timetable - Last event = '" + OneEntry + "'. An earlier finish event has been found with something other than a repeat following it - only a repeat can follow a finish event.");
10688  Utilities->CallLogPop(79);
10689  return(false);
10690  }
10691  if(OneEntry[1] != 'R') // must be finish
10692  {
10693  if(!SplitEntry(1, OneEntry, GiveMessages, CheckLocationsExistInRailway, First, Second, Third, Fourth, RearStartOrRepeatMins,
10694  FrontStartOrRepeatDigits, FormatType, LocationType, SequenceType, ShuttleLinkType, ExitList, Warning))
10695  {
10696  TimetableMessage(GiveMessages, "Error in timetable - Event: '" + OneEntry + "'");
10697  Utilities->CallLogPop(757);
10698  return(false);
10699  }
10700  if(SequenceType != Finish)
10701  {
10702  TimetableMessage(GiveMessages, "Error in timetable - last event should be a finish: '" + OneEntry + "'");
10703  Utilities->CallLogPop(785);
10704  return(false);
10705  }
10706  if(FinalCall)
10707  {
10708  // interpret & add to ActionVector
10709  TDateTime TempTime;
10710  TActionVectorEntry ActionVectorEntry;
10711  ActionVectorEntry.FormatType = FormatType;
10712  ActionVectorEntry.LocationType = LocationType;
10713  ActionVectorEntry.SequenceType = SequenceType;
10714  ActionVectorEntry.ShuttleLinkType = ShuttleLinkType;
10715  ActionVectorEntry.Warning = Warning;
10716  if(FormatType == TimeCmd)
10717  {
10718  if(CheckTimeValidity(11, First, ActionVectorEntry.EventTime))
10719  {
10720  ;
10721  }
10722  ActionVectorEntry.Command = Second;
10723  }
10724  else if(FormatType == TimeCmdHeadCode)
10725  {
10726  if(CheckTimeValidity(12, First, ActionVectorEntry.EventTime))
10727  {
10728  ;
10729  }
10730  ActionVectorEntry.Command = Second;
10731  ActionVectorEntry.OtherHeadCode = Third;
10732  }
10733  else if(FormatType == FNSNonRepeatToShuttle)
10734  {
10735  if(CheckTimeValidity(13, First, ActionVectorEntry.EventTime))
10736  {
10737  ;
10738  }
10739  ActionVectorEntry.Command = Second;
10740  ActionVectorEntry.NonRepeatingShuttleLinkHeadCode = Third;
10741  }
10742  else if(FormatType == FSHNewService)
10743  {
10744  if(CheckTimeValidity(14, First, ActionVectorEntry.EventTime))
10745  {
10746  ;
10747  }
10748  ActionVectorEntry.Command = Second;
10749  ActionVectorEntry.OtherHeadCode = Third;
10750  ActionVectorEntry.NonRepeatingShuttleLinkHeadCode = Fourth;
10751  }
10752  else if(FormatType == ExitRailway)
10753  {
10754  if(CheckTimeValidity(19, First, ActionVectorEntry.EventTime))
10755  {
10756  ;
10757  }
10758  ActionVectorEntry.Command = Second;
10759  ActionVectorEntry.ExitList = ExitList;
10760  }
10761  else if(FormatType == FinRemHere)
10762  {
10763  ActionVectorEntry.Command = Second;
10764  }
10765  TempTrainDataEntry.ActionVector.push_back(ActionVectorEntry);
10766  }
10767  }
10768  else // repeat
10769  {
10770  if(!SplitRepeat(0, OneEntry, RearStartOrRepeatMins, FrontStartOrRepeatDigits, NumberOfRepeats, GiveMessages))
10771  {
10772  Utilities->CallLogPop(786);
10773  // error messages given in SplitRepeat
10774  return(false);
10775  }
10776  if(FinalCall)
10777  {
10778  TActionVectorEntry ActionVectorEntry;
10779  ActionVectorEntry.FormatType = Repeat;
10780  ActionVectorEntry.LocationType = LocTypeForRepeatEntry;
10781  ActionVectorEntry.SequenceType = SequTypeForRepeatEntry;
10782  ActionVectorEntry.ShuttleLinkType = ShuttleLinkTypeForRepeatEntry;
10783  ActionVectorEntry.RearStartOrRepeatMins = RearStartOrRepeatMins;
10784  ActionVectorEntry.FrontStartOrRepeatDigits = FrontStartOrRepeatDigits;
10785  ActionVectorEntry.NumberOfRepeats = NumberOfRepeats;
10786  TempTrainDataEntry.ActionVector.push_back(ActionVectorEntry);
10787  }
10788  }
10789  }
10790  }
10791  if(FinalCall)
10792  {
10793  TrainDataVector.push_back(TempTrainDataEntry);
10794  }
10795  }
10796  Utilities->CallLogPop(80);
10797  return(true);
10798 }
10799 
10800 // ---------------------------------------------------------------------------
10801 
10802 bool TTrainController::Last2CharactersBothDigits(int Caller, AnsiString HeadCode)
10803 {
10804  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",Last2CharactersBothDigits," + HeadCode);
10805  if((HeadCode[HeadCode.Length() - 1] < '0') || (HeadCode[HeadCode.Length() - 1] > '9'))
10806  {
10807  Utilities->CallLogPop(1890);
10808  return(false);
10809  }
10810  if((HeadCode[HeadCode.Length()] < '0') || (HeadCode[HeadCode.Length()] > '9'))
10811  {
10812  Utilities->CallLogPop(1891);
10813  return(false);
10814  }
10815  Utilities->CallLogPop(1892);
10816  return(true);
10817 }
10818 
10819 // ---------------------------------------------------------------------------
10820 
10821 bool TTrainController::CheckTimeValidity(int Caller, AnsiString TimeStr, TDateTime &Time)
10822 // 1st 5 chars must be HH:MM, anything else will be ignored
10823 {
10824  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckTimeValidity," + TimeStr);
10825  if(TimeStr.Length() < 5)
10826  {
10827  Utilities->CallLogPop(926);
10828  return(false);
10829  }
10830  if((TimeStr[1] < '0') || (TimeStr[1] > '9'))
10831  {
10832  Utilities->CallLogPop(927);
10833  return(false);
10834  }
10835  if((TimeStr[2] < '0') || (TimeStr[2] > '9'))
10836  {
10837  Utilities->CallLogPop(928);
10838  return(false);
10839  }
10840  if(TimeStr[3] != ':')
10841  {
10842  Utilities->CallLogPop(929);
10843  return(false);
10844  }
10845  if((TimeStr[4] < '0') || (TimeStr[4] > '5'))
10846  {
10847  Utilities->CallLogPop(930);
10848  return(false);
10849  }
10850  if((TimeStr[5] < '0') || (TimeStr[5] > '9'))
10851  {
10852  Utilities->CallLogPop(931);
10853  return(false);
10854  }
10855  while(TimeStr.Length() > 5)
10856  {
10857  TimeStr = TimeStr.SubString(1, TimeStr.Length() - 1);
10858  }
10859  double WholeHours = (AnsiString(TimeStr[1]) + AnsiString(TimeStr[2])).ToDouble();
10860  double FracHour = ((AnsiString(TimeStr[4]) + AnsiString(TimeStr[5])).ToDouble()) / 60.0;
10861 
10862  if((WholeHours + FracHour) >= 95.98334)
10863  {
10864  Utilities->CallLogPop(1817);
10865  return(false); // > 95h 59m
10866  }
10867  Time = TDateTime((WholeHours + FracHour) / 24);
10868  Utilities->CallLogPop(932);
10869  return(true);
10870 }
10871 
10872 // ---------------------------------------------------------------------------
10873 
10874 bool TTrainController::SplitEntry(int Caller, AnsiString OneEntry, bool GiveMessages, bool CheckLocationsExistInRailway, AnsiString &First, AnsiString &Second,
10875  AnsiString &Third, AnsiString &Fourth, int &RearStartOrRepeatMins, int &FrontStartOrRepeatDigits, TTimetableFormatType &FormatType,
10876  TTimetableLocationType &LocationType, TTimetableSequenceType &SequenceType, TTimetableShuttleLinkType &ShuttleLinkType, TNumList &ExitList, bool &Warning)
10877 /* This is a train action entry from a single line of the timetable, i.e. not train information and not a repeat entry.
10878  Return false for failure.
10879  See description above under ProcessOneTimetableLinefor details of train action entries
10880  NB all types set except LocationType for Sns as may be located or not
10881 */{
10882  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SplitEntry," + OneEntry);
10883  Warning = false;
10884  TDateTime TempTime;
10885 
10886  if(OneEntry.Length() > 0)
10887  {
10888  if(OneEntry[1] == 'W') // warning
10889  {
10890  Warning = true;
10891  OneEntry = OneEntry.SubString(2, OneEntry.Length() - 1);
10892  // strip it off
10893  }
10894  }
10895  if(OneEntry == "Frh")
10896  {
10897  FormatType = FinRemHere;
10898  SequenceType = Finish;
10899  LocationType = AtLocation;
10900  ShuttleLinkType = NotAShuttleLink;
10901  Second = "Frh";
10902  Utilities->CallLogPop(1016);
10903  return(true);
10904  }
10905  if(OneEntry.Length() < 7)
10906  {
10907  Utilities->CallLogPop(907);
10908  return(false); // 'HH:MM;' + at least a one-letter location name
10909  }
10910  int Pos = OneEntry.Pos(';'); // first segment delimiter
10911 
10912  if(Pos != 6)
10913  {
10914  Utilities->CallLogPop(908);
10915  return(false);
10916  // no delimiter or delimiter not in position 6, has to be a time so fail
10917  }
10918  First = OneEntry.SubString(1, 5); // has to be a time
10919  if(!CheckTimeValidity(16, First, TempTime))
10920  {
10921  Utilities->CallLogPop(909);
10922  return(false);
10923  }
10924  AnsiString Remainder = OneEntry.SubString(Pos + 1, OneEntry.Length() - Pos);
10925 
10926  if((Remainder[1] >= '0') && (Remainder[1] <= '9'))
10927  // next segment is a time so this is a TimeTimeLoc & 3rd seg has to be a location to be valid
10928  {
10929  if(Remainder.Length() < 7)
10930  {
10931  Utilities->CallLogPop(910);
10932  return(false); // 'HH:MM;' + at least a one-letter location name
10933  }
10934  Pos = Remainder.Pos(';'); // second segment delimiter
10935  if(Pos == 0)
10936  {
10937  Utilities->CallLogPop(911);
10938  return(false);
10939  // no delimiter, has to be one between departure time & location
10940  }
10941  Second = Remainder.SubString(1, 5); // has to be a time
10942  if(!CheckTimeValidity(15, Second, TempTime))
10943  {
10944  Utilities->CallLogPop(912);
10945  return(false);
10946  }
10947  Third = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
10948  if(!CheckLocationValidity(0, Third, GiveMessages, CheckLocationsExistInRailway))
10949  {
10950  Utilities->CallLogPop(913);
10951  return(false);
10952  }
10953  FormatType = TimeTimeLoc;
10954  SequenceType = Intermediate;
10955  LocationType = AtLocation;
10956  ShuttleLinkType = NotAShuttleLink;
10957  Utilities->CallLogPop(914);
10958  return(true);
10959  }
10960  Pos = Remainder.Pos(';'); // second segment delimiter
10961  if(Pos == 0) // no third segment so second must be a location, or cdt
10962  {
10963  Second = Remainder;
10964  if(Second == "cdt")
10965  {
10966  FormatType = TimeCmd;
10967  ShuttleLinkType = NotAShuttleLink;
10968  LocationType = AtLocation;
10969  SequenceType = Intermediate;
10970  Utilities->CallLogPop(915);
10971  return(true);
10972  }
10973  if(!CheckLocationValidity(1, Second, GiveMessages, CheckLocationsExistInRailway))
10974  {
10975  Utilities->CallLogPop(916);
10976  return(false);
10977  }
10978  else
10979  {
10980  FormatType = TimeLoc;
10981  LocationType = AtLocation;
10982  SequenceType = Intermediate;
10983  ShuttleLinkType = NotAShuttleLink;
10984  Utilities->CallLogPop(917);
10985  return(true);
10986  }
10987  }
10988  // here if second segment is a command, with a third & maybe fourth segments as details
10989  if((Pos != 4) && (Pos != 7) && (Pos != 8))
10990  {
10991  Utilities->CallLogPop(918);
10992  return(false);
10993  // no third segement or not in position 4 or 7, & should be since all commands are 3, 6 or 7 letters
10994  }
10995  Second = Remainder.SubString(1, Pos - 1); // command
10996 
10997  Remainder = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
10998  // details
10999  Pos = Remainder.Pos(';'); // third segment delimiter
11000  if(Pos == 0)
11001  {
11002  Third = Remainder;
11003  }
11004  else
11005  {
11006  Third = Remainder.SubString(1, Pos - 1);
11007  Fourth = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
11008  }
11009  if((Second == "Snt") || (Second == "Snt-sh"))
11010  // third has to be 2 element idents with a space between
11011  {
11012  int SpacePos = Third.Pos(' ');
11013  if(SpacePos == 0)
11014  {
11015  Utilities->CallLogPop(919);
11016  return(false); // no space
11017  }
11018  AnsiString RearStartStr = Third.SubString(1, SpacePos - 1);
11019  AnsiString FrontStartStr = Third.SubString(SpacePos + 1, Third.Length() - SpacePos);
11020  // int RearPosition=0, FrontPosition=0, RearExitPos=0;
11021  if(CheckLocationsExistInRailway)
11022  {
11023  if(!CheckStartPositionValidity(0, RearStartStr, FrontStartStr, GiveMessages))
11024  {
11025  Utilities->CallLogPop(920);
11026  return(false);
11027  }
11028  RearStartOrRepeatMins = Track->GetTrackVectorPositionFromString(3, RearStartStr, GiveMessages);
11029  FrontStartOrRepeatDigits = Track->GetTrackVectorPositionFromString(4, FrontStartStr, GiveMessages);
11030  }
11031  if(Second == "Snt")
11032  {
11033  FormatType = StartNew;
11034  SequenceType = Start;
11035  LocationType = NoLocation;
11036  // can't be set until know whether located or not - done in SecondPassActions
11037  ShuttleLinkType = NotAShuttleLink;
11038  }
11039  else // Snt-sh
11040  {
11041  FormatType = SNTShuttle;
11042  LocationType = AtLocation;
11043  SequenceType = Start;
11044  ShuttleLinkType = ShuttleLink;
11045  if(!CheckHeadCodeValidity(0, GiveMessages, Fourth))
11046  {
11047  Utilities->CallLogPop(1038);
11048  return(false);
11049  }
11050  }
11051  Utilities->CallLogPop(921);
11052  return(true);
11053  }
11054  if(Second == "Sns-sh") // third & fourth have to be headcodes
11055  {
11056  FormatType = SNSShuttle;
11057  LocationType = AtLocation;
11058  SequenceType = Start;
11059  ShuttleLinkType = ShuttleLink;
11060  if(!CheckHeadCodeValidity(1, GiveMessages, Third))
11061  {
11062  Utilities->CallLogPop(1039);
11063  return(false);
11064  }
11065  if(!CheckHeadCodeValidity(2, GiveMessages, Fourth))
11066  {
11067  Utilities->CallLogPop(1040);
11068  return(false);
11069  }
11070  Utilities->CallLogPop(1041);
11071  return(true);
11072  }
11073  if(Second == "F-nshs")
11074  {
11075  FormatType = FNSNonRepeatToShuttle;
11076  LocationType = AtLocation;
11077  SequenceType = Finish;
11078  ShuttleLinkType = ShuttleLink;
11079  if(!CheckHeadCodeValidity(3, GiveMessages, Third))
11080  {
11081  Utilities->CallLogPop(1047);
11082  return(false);
11083  }
11084  Utilities->CallLogPop(1048);
11085  return(true);
11086  }
11087  if(Second == "Sns-fsh")
11088  {
11089  FormatType = SNSNonRepeatFromShuttle;
11090  LocationType = AtLocation;
11091  SequenceType = Start;
11092  ShuttleLinkType = ShuttleLink;
11093  if(!CheckHeadCodeValidity(4, GiveMessages, Third))
11094  {
11095  Utilities->CallLogPop(1098);
11096  return(false);
11097  }
11098  Utilities->CallLogPop(1099);
11099  return(true);
11100  }
11101  if(Second == "Fns-sh") // third & fourth have to be headcodes
11102  {
11103  FormatType = FSHNewService;
11104  LocationType = AtLocation;
11105  SequenceType = Finish;
11106  ShuttleLinkType = ShuttleLink;
11107  if(!CheckHeadCodeValidity(5, GiveMessages, Third))
11108  {
11109  Utilities->CallLogPop(1050);
11110  return(false);
11111  }
11112  if(!CheckHeadCodeValidity(6, GiveMessages, Fourth))
11113  {
11114  Utilities->CallLogPop(1051);
11115  return(false);
11116  }
11117  Utilities->CallLogPop(1052);
11118  return(true);
11119  }
11120  // new segment for 'pas'
11121  if(Second == "pas") // third has to be a location
11122  {
11123  FormatType = PassTime;
11124  LocationType = EnRoute;
11125  SequenceType = Intermediate;
11126  ShuttleLinkType = NotAShuttleLink;
11127  if(!CheckLocationValidity(2, Third, GiveMessages, CheckLocationsExistInRailway))
11128  {
11129  Utilities->CallLogPop(1515);
11130  return(false);
11131  }
11132  Utilities->CallLogPop(1516);
11133  return(true);
11134  }
11135  // new segment for revised 'Fer'
11136  if(Second == "Fer")
11137  // third has to be a set of IDs separated by spaces, and at least 1
11138  {
11139  FormatType = ExitRailway;
11140  LocationType = EnRoute;
11141  SequenceType = Finish;
11142  ShuttleLinkType = NotAShuttleLink;
11143  if(CheckLocationsExistInRailway)
11144  {
11145  if(!CheckAndPopulateListOfIDs(0, Third, ExitList, GiveMessages))
11146  {
11147  Utilities->CallLogPop(1519);
11148  return(false);
11149  }
11150  }
11151  Utilities->CallLogPop(1520);
11152  return(true);
11153  }
11154  // all remainder must be TimeCmdHeadCode types to be valid
11155  if((Second != "Fns") && (Second != "Fjo") && (Second != "jbo") && (Second != "fsp") && (Second != "rsp") && (Second != "Sfs") && (Second != "Sns") &&
11156  (Second != "Frh-sh"))
11157  {
11158  Utilities->CallLogPop(922);
11159  return(false); // all TimeCmdHeadCode types
11160  }
11161  if(!CheckHeadCodeValidity(7, GiveMessages, Third))
11162  {
11163  Utilities->CallLogPop(923);
11164  return(false);
11165  }
11166  FormatType = TimeCmdHeadCode;
11167  LocationType = AtLocation;
11168  if(Second == "Frh-sh")
11169  {
11170  ShuttleLinkType = ShuttleLink;
11171  }
11172  else
11173  {
11174  ShuttleLinkType = NotAShuttleLink;
11175  }
11176  if((Second == "Fns") || (Second == "Fjo") || (Second == "Frh-sh"))
11177  {
11178  SequenceType = Finish;
11179  }
11180  if((Second == "jbo") || (Second == "fsp") || (Second == "rsp"))
11181  {
11182  SequenceType = Intermediate;
11183  }
11184  if((Second == "Sfs") || (Second == "Sns"))
11185  {
11186  SequenceType = Start;
11187  }
11188  Utilities->CallLogPop(924);
11189  return(true);
11190 }
11191 
11192 // ---------------------------------------------------------------------------
11193 
11194 bool TTrainController::CheckLocationValidity(int Caller, AnsiString LocStr, bool GiveMessages, bool CheckLocationsExistInRailway)
11195 {
11196  // check that the location name exists in the railway (only if CheckLocationsExistInRailway is true), doesn't begin with a number
11197  // and contains no special characters
11198  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckLocationValidity," + LocStr);
11199  if(LocStr == "")
11200  {
11201  Utilities->CallLogPop(1353);
11202  return(false); // has to have at least one character
11203  }
11204  if((LocStr[1] >= '0') && (LocStr[1] <= '9'))
11205  {
11206  Utilities->CallLogPop(1354);
11207  return(false); // can't begin with a number
11208  }
11209  for(int x = 1; x < LocStr.Length() + 1; x++)
11210  {
11211  if(LocStr[x] < ' ')
11212  {
11213  Utilities->CallLogPop(1355);
11214  return(false); // contains a special character
11215  }
11216  if(LocStr[x] > 'z')
11217  {
11218  Utilities->CallLogPop(1356);
11219  return(false); // contains a character outside the standard ASCII set
11220  }
11221  }
11222  // check exists in railway location list if CheckLocationsExistInRailway is true
11223  if(CheckLocationsExistInRailway)
11224  {
11225  if(!Track->TimetabledLocationNameAllocated(3, LocStr))
11226  {
11227  TimetableMessage(GiveMessages, "Location name '" + LocStr +
11228  "' appears in the timetable but is not a valid name. To be valid the name must be a stopping location and apply to one or more platforms " +
11229  "(not concourses on their own), or to track at a blue non-station named location. BUT NOTE THAT trains can't stop at continuations so a name " +
11230  "that includes a continuation will not be valid.");
11231  Utilities->CallLogPop(1357);
11232  return(false);
11233  }
11234  }
11235  Utilities->CallLogPop(1358);
11236  return(true);
11237 }
11238 
11239 // ---------------------------------------------------------------------------
11240 
11241 bool TTrainController::CheckHeadCodeValidity(int Caller, bool GiveMessages, AnsiString HeadCode)
11242 {
11243  // if(!AnyHeadCodeValid) up to 8 characters total & last 4 characters must be NLNN where N = number and L = capital or small letter
11244  // if(AnyHeadCodeValid) up to 8 characters total, last 2 chars must be digits & last but 2 can be any alphanumeric, upper or lower case
11245  // NOTE: As of v0.6b AnyHeadCodeValid dropped, all headcodes are unrestricted
11246  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + "," + AnsiString((short)GiveMessages) + ",CheckHeadCodeValidity," +
11247  HeadCode);
11248  if((HeadCode.Length() < 4) || (HeadCode.Length() > 8))
11249  {
11250  TimetableMessage(GiveMessages, "Headcode error in '" + HeadCode +
11251  "', length must be between 4 and 8 characters, and last 4 must be a legitimate headcode. This error can also be caused by omitting a service reference after Sns, Snt-sh, Sns-sh, Fns, Fns-sh or Frh-sh");
11252  Utilities->CallLogPop(1359);
11253  return(false);
11254  }
11255  // firstly allow any printable character (ASCII >= CHAR(32) & <= CHAR(126)), as these allowed in 1st 4 characters
11256  for(int x = 1; x < (HeadCode.Length() + 1); x++)
11257  {
11258  if((HeadCode[x] < ' ') || (HeadCode[x] > '~'))
11259  {
11260  TimetableMessage(GiveMessages, "Non-printable character in headcode '" + HeadCode + "'");
11261  Utilities->CallLogPop(1895);
11262  return(false);
11263  }
11264  }
11265  // secondly ensure the true Headcode only has letters or digits
11266  for(int x = 3; x >= 0; x--)
11267  {
11268  if(((HeadCode[HeadCode.Length() - x] < 'A') || (HeadCode[HeadCode.Length() - x] > 'Z')) && ((HeadCode[HeadCode.Length() - x] < 'a') ||
11269  (HeadCode[HeadCode.Length() - x] > 'z')) && ((HeadCode[HeadCode.Length() - x] < '0') || (HeadCode[HeadCode.Length() - x] > '9')))
11270  {
11271  TimetableMessage(GiveMessages, "Headcode error in '" + HeadCode + "', headcode must consist of letters and digits only");
11272  Utilities->CallLogPop(1790);
11273  return(false);
11274  }
11275  }
11276  Utilities->CallLogPop(1364);
11277  return(true);
11278 }
11279 
11280 // ---------------------------------------------------------------------------
11281 
11282 bool TTrainController::CheckAndPopulateListOfIDs(int Caller, AnsiString IDSet, TNumList &ExitList, bool GiveMessages)
11283 // set of legitimate track element IDs, separated by spaces, and at least 1 present
11284 {
11285  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckSetOfIDs," + IDSet);
11286  ExitList.clear();
11287  AnsiString CurrentID = "";
11288 
11289  if(IDSet.Length() == 0)
11290  {
11291  TimetableMessage(GiveMessages, "Must have at least one exit element ID following 'Fer'");
11292  Utilities->CallLogPop(1521);
11293  return(false);
11294  }
11295  for(int x = 1; x <= IDSet.Length(); x++)
11296  {
11297  char C = IDSet[x];
11298  if(((C < '0') || (C > '9')) && (C != ' ') && (C != 'N') && (C != '-'))
11299  {
11300  TimetableMessage(GiveMessages, "Illegal character in the set of element IDs following 'Fer' in '" + IDSet + "'");
11301  Utilities->CallLogPop(1522);
11302  return(false);
11303  }
11304  }
11305  int Pos = IDSet.Pos(' '); // look for the first space
11306 
11307  while(true)
11308  {
11309  if(Pos == 0)
11310  {
11311  CurrentID = IDSet;
11312  IDSet = "";
11313  }
11314  else
11315  {
11316  CurrentID = IDSet.SubString(1, Pos - 1);
11317  IDSet = IDSet.SubString(Pos + 1, IDSet.Length() - Pos);
11318  }
11319  int VecPos = Track->GetTrackVectorPositionFromString(7, CurrentID, GiveMessages);
11320  if(VecPos == -1)
11321  {
11322  Utilities->CallLogPop(1523);
11323  return(false); // messages given in GetTrackVectorPositionFromString
11324  }
11325  else
11326  {
11327  if(Track->TrackElementAt(722, VecPos).TrackType != Continuation)
11328  {
11329  TimetableMessage(GiveMessages, "The element ID '" + CurrentID + "' following 'Fer' is not an exit");
11330  Utilities->CallLogPop(1524);
11331  return(false);
11332  }
11333  else
11334  {
11335  // first check for duplicates
11336  if(!ExitList.empty())
11337  {
11338  for(TNumListIterator ELIT = ExitList.begin(); ELIT != ExitList.end(); ELIT++)
11339  {
11340  if(*ELIT == VecPos)
11341  {
11342  TimetableMessage(GiveMessages, "The element ID '" + CurrentID + "' following 'Fer' duplicates an earlier element");
11343  Utilities->CallLogPop(1532);
11344  return(false);
11345  }
11346  }
11347  }
11348  // of OK add it to the list
11349  ExitList.push_back(VecPos);
11350  }
11351  }
11352  if(IDSet == "")
11353  {
11354  Utilities->CallLogPop(1525);
11355  return(true);
11356  }
11357  else
11358  {
11359  Pos = IDSet.Pos(' '); // look for the next space
11360  }
11361  } // while(true)
11362 }
11363 
11364 // ---------------------------------------------------------------------------
11365 bool TTrainController::SplitTrainInfo(int Caller, AnsiString TrainInfoStr, AnsiString &HeadCode, AnsiString &Description, int &StartSpeed, int &MaxRunningSpeed,
11366  int &Mass, double &MaxBrakeRate, double &PowerAtRail, int &SignallerSpeed, bool GiveMessages)
11367 // 7 or 8 items for a new train (6 or 7 semicolons), for a continuing service only need headcode, though can have a description, if other
11368 // data entered for continuing service then will be ignored - message given to warn user, checks appropriate number of items and validity
11369 // of each item
11370 {
11371  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SplitTrainInfo," + TrainInfoStr);
11372  int Pos = 0;
11373  AnsiString Remainder = "";
11374  int SemiColonCount = 0;
11375 
11376  for(int x = 1; x < TrainInfoStr.Length() + 1; x++)
11377  {
11378  if(TrainInfoStr[x] == ';')
11379  {
11380  SemiColonCount++;
11381  }
11382  }
11383  if((SemiColonCount != 6) && (SemiColonCount != 7) && (SemiColonCount != 1) && (SemiColonCount != 0))
11384  {
11385  TimetableMessage(GiveMessages, "Error in train information in '" + TrainInfoStr +
11386  "'. Should be headcode + optional description for a continuing service;" +
11387  " or headcode, description, start speed, max running speed, mass, brake force, power (and optional signaller max. speed) for a new service");
11388  Utilities->CallLogPop(880);
11389  return(false);
11390  }
11391  if(SemiColonCount == 0)
11392  {
11393  HeadCode = TrainInfoStr;
11394  if(!CheckHeadCodeValidity(8, GiveMessages, HeadCode))
11395  {
11396  Utilities->CallLogPop(881);
11397  return(false);
11398  }
11399  Utilities->CallLogPop(882);
11400  return(true);
11401  }
11402  if(SemiColonCount == 1) // headcode & description only
11403  {
11404  Pos = TrainInfoStr.Pos(';'); // 1st delimiter
11405  HeadCode = TrainInfoStr.SubString(1, Pos - 1);
11406  Description = TrainInfoStr.SubString(Pos + 1, TrainInfoStr.Length() - Pos);
11407  if(!CheckHeadCodeValidity(9, GiveMessages, HeadCode))
11408  {
11409  Utilities->CallLogPop(883);
11410  return(false);
11411  }
11412  if(Description == "")
11413  {
11414  TimetableMessage(GiveMessages, "Train description missing in '" + TrainInfoStr + "'");
11415  Utilities->CallLogPop(884);
11416  return(false);
11417  }
11418  if(Description.Length() > 60)
11419  {
11420  TimetableMessage(GiveMessages, "Train description too long, limit of 60 characters '" + TrainInfoStr + "'");
11421  Utilities->CallLogPop(1157);
11422  return(false);
11423  }
11424  for(int x = 1; x < Description.Length() + 1; x++)
11425  {
11426  if((Description[x] < ' ') || (Description[x] > '~'))
11427  {
11428  TimetableMessage(GiveMessages, "Train description contains invalid characters in '" + TrainInfoStr + "'");
11429  Utilities->CallLogPop(885);
11430  return(false);
11431  }
11432  }
11433  Utilities->CallLogPop(886);
11434  return(true);
11435  }
11436  // if here must have 6 or 7 semicolons
11437  Pos = TrainInfoStr.Pos(';'); // 1st delimiter
11438  HeadCode = TrainInfoStr.SubString(1, Pos - 1);
11439  Remainder = TrainInfoStr.SubString(Pos + 1, TrainInfoStr.Length() - Pos);
11440  if(!CheckHeadCodeValidity(10, GiveMessages, HeadCode))
11441  {
11442  Utilities->CallLogPop(887);
11443  return(false);
11444  }
11445  Pos = Remainder.Pos(';'); // 2nd delimiter
11446  Description = Remainder.SubString(1, Pos - 1);
11447  Remainder = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
11448  if(Description == "")
11449  {
11450  TimetableMessage(GiveMessages, "Train description missing in '" + TrainInfoStr + "'");
11451  Utilities->CallLogPop(888);
11452  return(false);
11453  }
11454  if(Description.Length() > 60)
11455  {
11456  TimetableMessage(GiveMessages, "Train description too long, limit of 60 characters '" + TrainInfoStr + "'");
11457  Utilities->CallLogPop(1158);
11458  return(false);
11459  }
11460  for(int x = 1; x < Description.Length() + 1; x++)
11461  {
11462  if((Description[x] < ' ') || (Description[x] > 126))
11463  {
11464  TimetableMessage(GiveMessages, "Train description contains invalid characters in '" + TrainInfoStr + "'");
11465  Utilities->CallLogPop(889);
11466  return(false);
11467  }
11468  }
11469  Pos = Remainder.Pos(';'); // 3rd delimiter
11470  AnsiString StartSpeedStr = Remainder.SubString(1, Pos - 1);
11471 
11472  Remainder = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
11473  if(StartSpeedStr == "")
11474  {
11475  TimetableMessage(GiveMessages, "Train starting speed missing in '" + TrainInfoStr + "'");
11476  Utilities->CallLogPop(890);
11477  return(false);
11478  }
11479  for(int x = 1; x < StartSpeedStr.Length() + 1; x++)
11480  {
11481  if((StartSpeedStr[x] < '0') || (StartSpeedStr[x] > '9'))
11482  {
11483  TimetableMessage(GiveMessages, "Train start speed contains invalid characters in '" + TrainInfoStr + "'");
11484  Utilities->CallLogPop(891);
11485  return(false);
11486  }
11487  }
11488  StartSpeed = StartSpeedStr.ToInt();
11489  if(StartSpeed > TTrain::MaximumSpeedLimit) // 400kph = 250mph
11490  {
11491  StartSpeed = TTrain::MaximumSpeedLimit;
11492  if(!SSHigh) // added at v2.4.0
11493  {
11494  TimetableMessage(GiveMessages, "Train starting speed > 400km/h in '" + TrainInfoStr + "'. Setting it to 400km/h");
11495  SSHigh = true;
11496  }
11497  }
11498  Pos = Remainder.Pos(';'); // 4th delimiter
11499  AnsiString MaxRunningSpeedStr = Remainder.SubString(1, Pos - 1);
11500 
11501  Remainder = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
11502  if(MaxRunningSpeedStr == "")
11503  {
11504  TimetableMessage(GiveMessages, "Train maximum running speed missing in '" + TrainInfoStr + "'");
11505  Utilities->CallLogPop(892);
11506  return(false);
11507  }
11508  for(int x = 1; x < MaxRunningSpeedStr.Length() + 1; x++)
11509  {
11510  if((MaxRunningSpeedStr[x] < '0') || (MaxRunningSpeedStr[x] > '9'))
11511  {
11512  TimetableMessage(GiveMessages, "Train maximum running speed contains invalid characters in '" + TrainInfoStr + "'");
11513  Utilities->CallLogPop(893);
11514  return(false);
11515  }
11516  }
11517  MaxRunningSpeed = MaxRunningSpeedStr.ToInt();
11518  if(MaxRunningSpeed > TTrain::MaximumSpeedLimit) // 400kph = 250mph
11519  {
11520  MaxRunningSpeed = TTrain::MaximumSpeedLimit;
11521  if(!MRSHigh) // added at v2.4.0
11522  {
11523  TimetableMessage(GiveMessages, "Train maximum running speed > 400km/h in '" + TrainInfoStr + "'. Setting it to 400km/h");
11524  MRSHigh = true;
11525  }
11526  }
11527  if(MaxRunningSpeed < 10)
11528  // changed at v0.6 to prevent low max speeds - can cause problems in SetTrainMovementValues
11529  {
11530  MaxRunningSpeed = 10;
11531  if(!MRSLow) // added at v2.4.0
11532  {
11533  TimetableMessage(GiveMessages, "Train maximum running speed can't be less than 10km/h in '" + TrainInfoStr + "', it will be set to 10km/h");
11534  MRSLow = true;
11535  }
11536  }
11537  Pos = Remainder.Pos(';'); // 5th delimiter
11538  AnsiString MassStr = Remainder.SubString(1, Pos - 1);
11539 
11540  Remainder = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
11541  if(MassStr == "")
11542  {
11543  TimetableMessage(GiveMessages, "Train mass missing in '" + TrainInfoStr + "'");
11544  Utilities->CallLogPop(895);
11545  return(false);
11546  }
11547  for(int x = 1; x < MassStr.Length() + 1; x++)
11548  {
11549  if((MassStr[x] < '0') || (MassStr[x] > '9'))
11550  {
11551  TimetableMessage(GiveMessages, "Train mass contains invalid characters in '" + TrainInfoStr + "'");
11552  Utilities->CallLogPop(896);
11553  return(false);
11554  }
11555  }
11556  Mass = MassStr.ToInt() * 1000; // convert tonnes to kg
11557  if(Mass > TTrain::MaximumMassLimit) // 10,000tonnes
11558  {
11559  Mass = TTrain::MaximumMassLimit;
11560  if(!MassHigh) // added at v2.4.0
11561  {
11562  TimetableMessage(GiveMessages, "Train mass > 10,000 tonnes in '" + TrainInfoStr + "'. Setting it to 10,000 tonnes");
11563  MassHigh = true;
11564  }
11565  }
11566  if(Mass == 0)
11567  {
11568  TimetableMessage(GiveMessages, "Train mass zero in '" + TrainInfoStr + "'");
11569  Utilities->CallLogPop(897);
11570  return(false);
11571  }
11572  Pos = Remainder.Pos(';'); // 6th delimiter
11573  AnsiString MaxBrakeForceStr = Remainder.SubString(1, Pos - 1);
11574 
11575  Remainder = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
11576  if(MaxBrakeForceStr == "")
11577  {
11578  TimetableMessage(GiveMessages, "Train braking force missing in '" + TrainInfoStr + "'");
11579  Utilities->CallLogPop(898);
11580  return(false);
11581  }
11582  for(int x = 1; x < (MaxBrakeForceStr.Length() + 1); x++)
11583  {
11584  if((MaxBrakeForceStr[x] != '.') && ((MaxBrakeForceStr[x] < '0') || (MaxBrakeForceStr[x] > '9')))
11585  {
11586  TimetableMessage(GiveMessages, "Train braking force contains invalid characters in '" + TrainInfoStr + "'");
11587  Utilities->CallLogPop(899);
11588  return(false);
11589  }
11590  }
11591  double MaxBrakeForce = MaxBrakeForceStr.ToDouble() * 1000;
11592 
11593  // convert to kg force
11594  if((MaxBrakeForce / Mass) > 1) // gives 'g' braking - 9.81m/s/s
11595  {
11596  MaxBrakeForce = Mass;
11597  if(!BFHigh) // added at v2.4.0
11598  {
11599  TimetableMessage(GiveMessages, "Train braking force too high in '" + TrainInfoStr + "'. Setting it to the same as the train mass");
11600  BFHigh = true;
11601  }
11602  }
11603  if((MaxBrakeForce / Mass) < 0.01)
11604  {
11605  MaxBrakeForce = Mass * 0.01;
11606  if(!BFLow) // added at v2.4.0
11607  {
11608  TimetableMessage(GiveMessages, "Train braking force too low in '" + TrainInfoStr + "'. Setting it to 1% of the train mass");
11609  BFLow = true;
11610  }
11611  }
11612  // convert to m/s/s
11613  MaxBrakeRate = MaxBrakeForce / Mass * 9.81;
11614  // now may have just a power entry or power and signaller max. speed
11615  AnsiString GrossPowerStr = "", SignallerSpeedStr = "";
11616 
11617  if(SemiColonCount == 6)
11618  {
11619  GrossPowerStr = Remainder;
11620  SignallerSpeedStr = "30"; // default value
11621  }
11622  else // must be 7
11623  {
11624  Pos = Remainder.Pos(';'); // 7th delimiter
11625  GrossPowerStr = Remainder.SubString(1, Pos - 1);
11626  SignallerSpeedStr = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
11627  }
11628  // deal with GrossPower
11629  if(GrossPowerStr == "")
11630  {
11631  TimetableMessage(GiveMessages, "Train power missing in '" + TrainInfoStr + "'");
11632  Utilities->CallLogPop(901);
11633  return(false);
11634  }
11635  for(int x = 1; x < GrossPowerStr.Length() + 1; x++)
11636  {
11637  if((GrossPowerStr[x] < '0') || (GrossPowerStr[x] > '9'))
11638  {
11639  TimetableMessage(GiveMessages, "Train power contains invalid characters in '" + TrainInfoStr + "'");
11640  Utilities->CallLogPop(902);
11641  return(false);
11642  }
11643  }
11644 
11645  double GrossPower = GrossPowerStr.ToInt() * 1000; // convert to W
11646 
11647  if(GrossPower > TTrain::MaximumPowerLimit) // 100MW
11648  {
11649  GrossPower = TTrain::MaximumPowerLimit;
11650  if(!PwrHigh)
11651  {
11652  TimetableMessage(GiveMessages, "Train power > 100,000kW in '" + TrainInfoStr + "'. Setting it to 100,000kW");
11653  PwrHigh = true;
11654  }
11655  }
11656  else if(GrossPower == 0) // changed at v2.4.0
11657  {
11658  GrossPower = 0.1;
11659  // can't be zero or AValue is zero and then have divide by zero error, so set to 0.1W so acceleration tiny (though should be intercepted before accel calculated)
11660  }
11661  else if((GrossPower > 0) && (GrossPower < 10000))
11662  // added at v2.4.0 to ensure min power of 8kW at rail unless zero (otherwise could have too low AValues
11663  {
11664  GrossPower = 10000;
11665  }
11666  PowerAtRail = GrossPower * 0.8;
11667  // apply ratio of 80% for rail to gross power (seems about average from an internet search)
11668 
11669  // deal with SignallerSpeed
11670  if(SignallerSpeedStr == "")
11671  {
11672  TimetableMessage(GiveMessages, "Signaller speed not set in '" + TrainInfoStr + "', either set a value or remove the extra semicolon");
11673  Utilities->CallLogPop(1771);
11674  return(false);
11675  }
11676  for(int x = 1; x < SignallerSpeedStr.Length() + 1; x++)
11677  {
11678  if((SignallerSpeedStr[x] < '0') || (SignallerSpeedStr[x] > '9'))
11679  {
11680  TimetableMessage(GiveMessages, "Signaller speed contains invalid characters in '" + TrainInfoStr + "'");
11681  Utilities->CallLogPop(1769);
11682  return(false);
11683  }
11684  }
11685  SignallerSpeed = SignallerSpeedStr.ToInt();
11686  if(SignallerSpeed > TTrain::MaximumSpeedLimit)
11687  {
11688  SignallerSpeed = TTrain::MaximumSpeedLimit;
11689  if(!SigSHigh)
11690  {
11691  TimetableMessage(GiveMessages, "Signaller speed > 400km/h in '" + TrainInfoStr + "'. Setting it to 400km/h");
11692  SigSHigh = true;
11693  }
11694  }
11695  if(SignallerSpeed < 10)
11696  // changed at v0.6 to prevent low max speeds - can cause problems in SetTrainMovementValues
11697  {
11698  SignallerSpeed = 10;
11699  if(!SigSLow)
11700  {
11701  TimetableMessage(GiveMessages, "Signaller speed can't be less than 10km/h in '" + TrainInfoStr + "', it will be set to 10km/h");
11702  SigSLow = true;
11703  }
11704  // Utilities->CallLogPop(1770);
11705  // return false;
11706  }
11707  Utilities->CallLogPop(904);
11708  return(true);
11709 }
11710 
11711 // ---------------------------------------------------------------------------
11712 
11713 bool TTrainController::SplitRepeat(int Caller, AnsiString OneEntry, int &RearStartOrRepeatMins, int &FrontStartOrRepeatDigits, int &NumberOfRepeats,
11714  bool GiveMessages)
11715 {
11716  // Format must be: R;mm;dd;nn mm may be 1, 2 or more digits, dd may be 1 or 2 digits, nn may be 1, 2 or more digits
11717  // function checks validity of each item and returns false for error
11718  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SplitRepeat," + OneEntry);
11719  if(OneEntry.Length() < 7)
11720  {
11721  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - should be 'R;m;d;n'");
11722  Utilities->CallLogPop(865);
11723  return(false);
11724  }
11725  int SemiColonCount = 0;
11726 
11727  for(int x = 1; x < OneEntry.Length() + 1; x++)
11728  {
11729  if(OneEntry[x] == ';')
11730  {
11731  SemiColonCount++;
11732  }
11733  }
11734  if(SemiColonCount != 3)
11735  {
11736  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - should be 'R;m;d;n'");
11737  Utilities->CallLogPop(866);
11738  return(false);
11739  }
11740  if((OneEntry[1] != 'R') || (OneEntry[2] != ';'))
11741  {
11742  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - should be 'R;m;d;n'");
11743  Utilities->CallLogPop(867);
11744  return(false);
11745  }
11746  AnsiString Remainder = OneEntry.SubString(3, OneEntry.Length() - 2);
11747  // strip off R;
11748 
11749  int Pos = 0;
11750 
11751  Pos = Remainder.Pos(';');
11752  AnsiString MinutesStr = Remainder.SubString(1, Pos - 1);
11753 
11754  Remainder = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
11755  if(MinutesStr == "")
11756  {
11757  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - minute increment segment missing");
11758  Utilities->CallLogPop(868);
11759  return(false);
11760  }
11761  if(MinutesStr.Length() > 3)
11762  // added for v2.3.1 following Albie Vowles' reported error in repeat value 03/02/20
11763  {
11764  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - minute value too high, maximum value is 999");
11765  Utilities->CallLogPop(2119);
11766  return(false);
11767  }
11768  for(int x = 1; x < MinutesStr.Length() + 1; x++)
11769  {
11770  if((MinutesStr[x] < '0') || (MinutesStr[x] > '9'))
11771  {
11772  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - non-digit character in minute increment segment");
11773  Utilities->CallLogPop(869);
11774  return(false);
11775  }
11776  }
11777  RearStartOrRepeatMins = MinutesStr.ToInt();
11778  if(RearStartOrRepeatMins == 0)
11779  {
11780  TimetableMessage(GiveMessages, "Repeat minute increment is zero in: '" + OneEntry + "' - can't have a zero value");
11781  Utilities->CallLogPop(870);
11782  return(false);
11783  }
11784  Pos = Remainder.Pos(';');
11785  AnsiString DigitsStr = Remainder.SubString(1, Pos - 1);
11786 
11787  Remainder = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
11788  if(DigitsStr == "")
11789  {
11790  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - headcode increment segment missing");
11791  Utilities->CallLogPop(871);
11792  return(false);
11793  }
11794  for(int x = 1; x < DigitsStr.Length() + 1; x++)
11795  {
11796  if((DigitsStr[x] < '0') || (DigitsStr[x] > '9'))
11797  {
11798  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - non-digit character in headcode increment segment");
11799  Utilities->CallLogPop(872);
11800  return(false);
11801  }
11802  }
11803  if(DigitsStr.Length() > 2)
11804  {
11805  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - maximum number of digits for headcode increment is 2");
11806  Utilities->CallLogPop(873);
11807  return(false);
11808  }
11809  FrontStartOrRepeatDigits = DigitsStr.ToInt();
11810 /* allow zero digit increments so HC can stay same for repeated services - for many suburban services the headcode digits relate to the
11811  route rather than the service
11812  if(FrontStartOrRepeatDigits == 0)
11813  {
11814  TimetableMessage(GiveMessages, "Repeat headcode increment is zero in: '" + OneEntry + "' - can't have a zero value");
11815  Utilities->CallLogPop(874);
11816  return false;
11817  }
11818 */
11819  if(!Last2CharactersBothDigits(0, ServiceReference) && (FrontStartOrRepeatDigits > 0))
11820  // new for v0.6b for unrestricted headcodes
11821  {
11822  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry +
11823  "' - a repeating service with incrementing digits must have digits as its last two headcode characters");
11824  Utilities->CallLogPop(1889);
11825  return(false);
11826  }
11827  AnsiString NumberStr = Remainder;
11828 
11829  if(NumberStr == "")
11830  {
11831  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - number of repeats missing");
11832  Utilities->CallLogPop(875);
11833  return(false);
11834  }
11835  if(NumberStr.Length() > 4)
11836  // added for v2.3.1 following Albie Vowles' reported error 03/02/20
11837  {
11838  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - repeat value too high, no timetabled event can exceed 95 hours & 59 minutes");
11839  Utilities->CallLogPop(2118);
11840  return(false);
11841  }
11842  for(int x = 1; x < NumberStr.Length() + 1; x++)
11843  {
11844  if((NumberStr[x] < '0') || (NumberStr[x] > '9'))
11845  // catches negative numbers
11846  {
11847  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - non-digit character in number of repeats");
11848  Utilities->CallLogPop(876);
11849  return(false);
11850  }
11851  }
11852  NumberOfRepeats = NumberStr.ToInt();
11853  if(NumberOfRepeats == 0)
11854  {
11855  TimetableMessage(GiveMessages, "Number of repeats is zero in: '" + OneEntry + "' - if no repeats are needed the repeat should be omitted");
11856  Utilities->CallLogPop(877);
11857  return(false);
11858  }
11859  Utilities->CallLogPop(878);
11860  return(true);
11861 }
11862 
11863 // ---------------------------------------------------------------------------
11864 
11865 bool TTrainController::SecondPassActions(int Caller, bool GiveMessages, bool &TwoLocationFlag) //TwoLocationFlag added at v2.9.1
11866 /* Note that here the TrainDataVector has been compiled with FinalCall true in ProcessOneTimetableLine so work on the
11867  vector rather than the timetable
11868  Note also that for unlocated Snt entries the LocationType hasn't yet been set
11869 
11870  Many of the errors caught here duplicate those in the preliminary checks, but leave in for completeness
11871 
11872  For info:-
11873  class TActionVectorEntry //contains a single train action - repeat entry is also of this class though no train action is taken for it
11874  {
11875  public:
11876  AnsiString LocationName, Command, OtherHeadCode, NonRepeatingShuttleLinkHeadCode; ///< string values for timetabled event entries, null
11878  bool SignallerControl; ///< indicates a train that is defined by the timetable as under signaller control
11879  bool Warning; ///< if set triggers an alert in the warning panel when the action is reached
11880  int NumberOfRepeats; ///< the number of repeating services
11881  int RearStartOrRepeatMins, FrontStartOrRepeatDigits; ///< dual-purpose variables used for the TrackVectorPositions of the rear and front
11883  TDateTime EventTime, ArrivalTime, DepartureTime; ///< relevant times at which the action is timetabled, zeroed on creation so change
11885  TNumList ExitList; ///< the list of valid train exit TrackVector positions for 'Fer' entries (empty to begin with)
11886  TTimetableFormatType FormatType; ///< defines the timetable action type
11887  TTimetableLocationType LocationType; ///< indicates where the train is when the relevant action occurs
11888  TTimetableSequenceType SequenceType; ///< indicates where in the sequence of codes the action lies
11889  TTimetableShuttleLinkType ShuttleLinkType; ///< indicates whether or not the action relates to a shuttle service link
11890  TTrainDataEntry *LinkedTrainEntryPtr; ///< link pointer for use between fsp/rsp & Sfs; Fjo & jbo; Fns & Sns; & all shuttle to shuttle
11892  TTrainDataEntry *NonRepeatingShuttleLinkEntryPtr; ///< pointer used by shuttles for the non-shuttle train links, in & out, the
11894 
11895  // inline function
11896 
11898  TActionVectorEntry() {
11899  RearStartOrRepeatMins=0; FrontStartOrRepeatDigits=0; NumberOfRepeats=0; FormatType=NoFormat;
11900  SequenceType=NoSequence; LocationType=NoLocation; ShuttleLinkType=NoShuttleLink, EventTime=TDateTime(-1);
11901  ArrivalTime=TDateTime(-1); DepartureTime=TDateTime(-1); LinkedTrainEntryPtr=0; NonRepeatingShuttleLinkEntryPtr=0;
11902  Warning = false; SignallerControl = false;
11903  }
11904  };
11905 
11906  typedef std::vector<TActionVectorEntry> TActionVector;//contains all actions for a single train
11907 
11908  class TTrainDataEntry //contains all data for a single train - copied into train object when becomes active
11909  {
11910  public:
11911  AnsiString HeadCode, ServiceReference, Description; ///< headcode is the first train's headcode, rest are calculated from repeat
11914  double MaxBrakeRate; ///< in metres/sec/sec
11915  double MaxRunningSpeed; ///< in km/h
11916  double PowerAtRail; ///< in Watts (taken as 80% of the train's Gross Power, i.e. that entered by the user)
11917  int Mass; ///< in kg
11918  int NumberOfTrains; ///< number of repeats + 1
11919  int SignallerSpeed; ///< in km/h for use when under signaller control
11920  int StartSpeed; ///< in km/h
11921  TActionVector ActionVector; ///< all the actions for the train
11922  TTrainOperatingDataVector TrainOperatingDataVector; ///< operating information for the train including all its repeats
11923 
11924  //inline function
11925 
11927  TTrainDataEntry()
11928  {
11929  StartSpeed=0; MaxRunningSpeed=0; NumberOfTrains=0;
11930  }
11931  };
11932 
11933  Allowable successors:-
11934  Snt unlocated -> Fer, TimeLoc (arr), TimeTimeLoc, (new) pas; No others
11935  Snt located -> No starts, no finishes except Frh & Fjo (as of v2.0.0), no repeat, pas or TimeTimeLoc; any other cmd or TimeLoc (dep) OK
11936  Snt-sh -> No starts, finishes, repeats, pas or TimeTimeLoc; any other cmd or TimeLoc (dep) OK
11937  Sfs -> No starts, finishes, repeats, pas or TimeTimeLoc; any other cmd or TimeLoc (dep) OK (must have a TimeLoc departure somewhere in sequence to
11938  set location, else fails)
11939  Sns -> No starts, finishes, repeats, pas or TimeTimeLoc; any other cmd or TimeLoc (dep) OK (must have a TimeLoc departure somewhere in sequence to
11940  set location, else fails)
11941  Sns-sh -> No starts, finishes, repeats, pas or TimeTimeLoc; any other cmd or TimeLoc (dep) OK (must have a TimeLoc departure somewhere in sequence to
11942  set location, else fails)
11943  Sns-fsh -> No starts, finishes, repeats, pas or TimeTimeLoc; any other cmd or TimeLoc (dep) OK (must have a TimeLoc departure somewhere in sequence to
11944  set location, else fails)
11945  Fns -> R only
11946  F-nshs -> Nothing (no repeats permitted)
11947  Fjo -> R only
11948  Frh -> R only
11949  Fer -> R only
11950  Frh-sh -> R only
11951  Fns-sh -> R only
11952  jbo -> No starts, finishes, repeats, splits, pas or TimeTimeLoc; TimeLoc (dep), jbo or cdt OK
11953  fsp -> No starts, repeats, Fer, pas or TimeTimeLoc; TimeLoc (dep) or any other OK
11954  rsp -> No starts, repeats, Fer, pas or TimeTimeLoc; TimeLoc (dep) or any other OK
11955  cdt -> No starts, repeats, Fer, pas or TimeTimeLoc; TimeLoc (dep) or any other OK
11956  TimeLoc (arr) -> No starts, repeats, Fer, pas or TimeTimeLoc; TimeLoc (dep) or any other OK
11957  TimeLoc (dep) -> Fer, TimeLoc (arr), or TimeTimeLoc, (new) pas OK, no others
11958  TimeTimeLoc -> Fer, TimeLoc (arr), or TimeTimeLoc, (new) pas OK, no others
11959  (new) pas -> Fer, TimeLoc (arr), or TimeTimeLoc, (new) pas OK, no others
11960  Repeat -> Nothing
11961 
11962  There must be a TimeLoc arrival (or a Sns start at location) in a sequence so successive cmd locations can be set
11963  Check all Snt's & set Locations if located (located = zero start speed, either element at a location (but if rear element
11964  is a continuation then treated as unlocated), and location listed in the next TimeLoc entry, though needn't be immediately after)
11965  If Snt entry at a location specified in a following TimeLoc entry but start speed > 0 give error message
11966  Check all times increase or stay same through ActionVector
11967  Cycle through all entries in vector setting arr & dep times based on above list
11968  Add locations to all relevant cmd entries based on earlier arrival location (or earlier reference for Sfs & Sns)
11969  Check locations match the arr & dep TimeLoc entries
11970  Check same location doesn't appear twice before a cdt except for separate arr & dep TimeLocs
11971  Make above valid succession checks
11972  Check all splits have matching Sfs headcodes (both ways), add locations to SFSs & check times same
11973  Check all new service headcodes (Sns) have matching headcodes (both ways), add locations to SNHs & check times same
11974  Check a split to 'x' doesn't again split to 'x' (anywhere, not just for one train, since headcodes can be duplicated)
11975  Check each Fns has matching Sns headcodes (both ways), add locations to SNHs & check times same
11976  Check all joins have matching headcodes (both ways), locations & times & don't occur in same sequence
11977  Check each joined by train not joined by same train again (anywhere, not just for one train, since headcodes can be duplicated)
11978  Set train info for Sfs & Sns entries
11979  Check each repeat entry exactly matches any included joins or splits (user has to enter it to show that really wants it)
11980  Check at least one platform long enough for a split (only need 2 lengths) & disallow if not, need length of 2 & 1 extra
11981  element at each end, or length of 3 & 1 extra element at either end
11982  Check all TimeLocs have either Arr or Dep times set and EventTime == -1
11983  Check all Cmds have EventTime set & Arr & Dep times = -1
11984  Check all Sfs & Sns entries followed somewhere in sequence by a TimeLoc departure
11985  Check all locations except unlocated Snts, Fers and Repeats have a LocationName
11986 
11987  Give messages in function if errors detected and clear the vector. Return false for failure.
11988 */
11989 
11990 /* Earlier checks:-
11991  Checks carried out with error messages in this function:-
11992  At least one comma in the line (it's based on a csv file);
11993  No entries following train information;
11994  At least one comma in remainder after train information (i.e at least a start and a finish entry);
11995  SplitEntry returns false in an intermediate entry - message repeats the entry for information;
11996  First entry not a start entry;
11997  Train information incomplete before a start entry;
11998  Entry follows a finish entry but doesn't begin with 'R';
11999  SplitEntry returns false in a finish entry - message repeats the entry for information;
12000  Last action entry isn't a finish entry.
12001 
12002  Function returns false with no message if:-
12003  Timetable start time invalid (no message is given for an invalid time as the line is assumed to be an irrelevant line; if no start
12004  time is found at all then an error message is given in the calling function);
12005  SplitTrainInfo returns false (message given in called function);
12006  SplitRepeat returns false (message given in called function).
12007 
12008 Double crosslink (shuttle) table:
12009 
12010 Command Format OtherHead NonRepeating- LinkTrain- NonRepeating- Decsription
12011  Code ShuttleLink- EntryPtr ShuttleLink-
12012  HeadCode EntryPtr
12013 
12014 Snt-sh SNTShuttle Y (rtn shuttle) N Y (rtn sh) N Simple shuttle - no feeder service
12015 Frh-sh TimeCmdHeadCode Y (outwd shuttle) N Y (outwd sh) N Simple shuttle - no finishing service
12016 F-nshs FNSNonRepeatToShuttle N (shld be Y for outwd shuttle) Y (shld be N) Y (correct) N (correct) Feeder service link to shuttle
12017 Sns-sh SNSShuttle Y (rtn shuttle) Y (feeder) Y (rtn) Y (fdr) Shuttle link from feeder service
12018 Sns-fsh SNSNonRepeatFromShuttle N (shld be Y for rtn shuttle) Y (shld be N) Y (correct) N (correct) Finishing service link from shuttle
12019 Fns-sh FSHNewService Y (outwd shuttle) Y (finishing) Y (outwd sh) Y (finish) Shuttle link to finishing service
12020 
12021 Note: Any shuttle start can have any finish - feeder and finish, neither, feeder but no finish & vice versa.
12022 
12023 */{
12024  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SecondPassActions,");
12025  if(TrainDataVector.empty())
12026  {
12027  SecondPassMessage(GiveMessages, "Error in timetable - there appear to be no train services in the timetable, it must contain at least one");
12028  TrainDataVector.clear();
12029  Utilities->CallLogPop(1832);
12030  return(false);
12031  }
12032 /* new preliminary checks for v0.2b without changing anything, carry each out separately:-
12033  1) must have at least one actionvector entry
12034  2) if first actionvector entry not SignallerControl then must have at least one more actionvector entry
12035  3) if first actionvector entry is SignallerControl then must have no more actionvector entries except a repeat
12036  4) first entry must be a start;
12037  4a) if first entry is Snt and not signallercontrol and second is a finish then it must be at a location with zero start speed
12038  5) a start must be the first entry;
12039  6) a repeat entry must be the last;
12040  7) for other than SignallerControl the last entry must be repeat or finish; if last entry is a repeat the last but one must be a finish;
12041  8) a finish entry must be the last or last but one, and if last but one the last must be a repeat
12042  Other successor errors will be caught later as all 'throws' changed to messages prior to the bulk of the sucessor checks
12043 */
12044 
12045  TwoLocationList.clear(); //empty the list to begin with, added at v2.9.1
12046  TwoLocationFlag = false; //added at v2.9.1
12047  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // (1)
12048  {
12049  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12050  if(TrainDataVector.at(x).ActionVector.empty())
12051  {
12052  SecondPassMessage(GiveMessages, "Error in timetable - the following service has no listed events, there must be at least one: " + TDEntry.HeadCode);
12053  TrainDataVector.clear();
12054  Utilities->CallLogPop(1833);
12055  return(false);
12056  }
12057  }
12058  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // (2)
12059  {
12060  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12061  TActionVectorEntry AVEntry0 = TrainDataVector.at(x).ActionVector.at(0);
12062  if(!(AVEntry0.SignallerControl))
12063  {
12064  if(TrainDataVector.at(x).ActionVector.size() == 1)
12065  {
12066  SecondPassMessage(GiveMessages, "Error in timetable - service must have a start event and at least one other for: " + TDEntry.HeadCode);
12067  TrainDataVector.clear();
12068  Utilities->CallLogPop(1822);
12069  return(false);
12070  }
12071  }
12072  }
12073  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // (3)
12074  {
12075  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12076  TActionVectorEntry AVEntry0 = TrainDataVector.at(x).ActionVector.at(0);
12077  if(AVEntry0.SignallerControl)
12078  {
12079  if(TrainDataVector.at(x).ActionVector.size() > 2)
12080  {
12081  SecondPassMessage(GiveMessages,
12082  "Error in timetable - a signaller control service can have no more than one item (a repeat) after the start event, see: " +
12083  TDEntry.HeadCode);
12084  TrainDataVector.clear();
12085  Utilities->CallLogPop(1837);
12086  return(false);
12087  }
12088  if(TrainDataVector.at(x).ActionVector.size() > 1)
12089  {
12090  TActionVectorEntry AVEntry1 = TrainDataVector.at(x).ActionVector.at(1);
12091  if(AVEntry1.FormatType != Repeat)
12092  {
12093  SecondPassMessage(GiveMessages,
12094  "Error in timetable - a signaller control service cannot have any other than a repeat after the start event, see: " + TDEntry.HeadCode);
12095  TrainDataVector.clear();
12096  Utilities->CallLogPop(1838);
12097  return(false);
12098  }
12099  }
12100  }
12101  }
12102  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // (4)
12103  {
12104  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12105  TActionVectorEntry AVEntry0 = TrainDataVector.at(x).ActionVector.at(0);
12106  if(AVEntry0.SequenceType != Start)
12107  {
12108  SecondPassMessage(GiveMessages, "Error in timetable - the first event must be a start for: " + TDEntry.HeadCode);
12109  TrainDataVector.clear();
12110  Utilities->CallLogPop(1824);
12111  return(false);
12112  }
12113  if((AVEntry0.Command == "Snt") && !(AVEntry0.SignallerControl))
12114  // 4a added at v2.0.0. This is only a rough check, Fer only valid for an unlocated Snt
12115  // and others for a located Snt, but those checks done later
12116  {
12117  TActionVectorEntry AVEntry1 = TrainDataVector.at(x).ActionVector.at(1);
12118  // must be a second entry if first not signallercontrol
12119  if((AVEntry1.SequenceType == Finish) && ((AVEntry1.Command == "Fns-sh") || (AVEntry1.Command == "Frh-sh")))
12120  {
12121  SecondPassMessage(GiveMessages, "Error in timetable - finish events Fns-sh and Frh-sh not permitted immediately after an Snt entry for: " +
12122  TDEntry.HeadCode);
12123  TrainDataVector.clear();
12124  Utilities->CallLogPop(2046);
12125  return(false);
12126  }
12127  }
12128  }
12129  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // (5)
12130  {
12131  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12132  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12133  {
12134  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12135  if((AVEntry.SequenceType == Start) && (y != 0))
12136  {
12137  SecondPassMessage(GiveMessages, "Error in timetable - a start event is present that is not the first event for: " + TDEntry.HeadCode);
12138  TrainDataVector.clear();
12139  Utilities->CallLogPop(1825);
12140  return(false);
12141  }
12142  }
12143  }
12144  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // (6)
12145  {
12146  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12147  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12148  {
12149  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12150  if((AVEntry.FormatType == Repeat) && (y != (TrainDataVector.at(x).ActionVector.size() - 1)))
12151  {
12152  SecondPassMessage(GiveMessages, "Error in timetable - a repeat is present that is not the last item for: " + TDEntry.HeadCode);
12153  TrainDataVector.clear();
12154  Utilities->CallLogPop(1826);
12155  return(false);
12156  }
12157  }
12158  }
12159  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // (7)
12160  {
12161  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12162  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12163  {
12164  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12165  if((y == 0) && AVEntry.SignallerControl)
12166  {
12167  break;
12168  }
12169  if(y == (TrainDataVector.at(x).ActionVector.size() - 1))
12170  {
12171  if((AVEntry.FormatType != Repeat) && (AVEntry.SequenceType != Finish))
12172  {
12173  SecondPassMessage(GiveMessages, "Error in timetable - the last item must be either a finish event or a repeat for: " + TDEntry.HeadCode);
12174  TrainDataVector.clear();
12175  Utilities->CallLogPop(1827);
12176  return(false);
12177  }
12178  if(AVEntry.FormatType == Repeat)
12179  {
12180  const TActionVectorEntry &LastAVEntry = TrainDataVector.at(x).ActionVector.at(y - 1);
12181  if(LastAVEntry.SequenceType != Finish)
12182  {
12183  SecondPassMessage(GiveMessages, "Error in timetable - the last event before the repeat must be a finish for: " + TDEntry.HeadCode);
12184  TrainDataVector.clear();
12185  Utilities->CallLogPop(1828);
12186  return(false);
12187  }
12188  }
12189  }
12190  }
12191  }
12192  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // (8)
12193  {
12194  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12195  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12196  {
12197  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12198  if(AVEntry.SequenceType == Finish)
12199  {
12200  if((y != (TrainDataVector.at(x).ActionVector.size() - 1)) && (y != (TrainDataVector.at(x).ActionVector.size() - 2)))
12201  {
12202  SecondPassMessage(GiveMessages, "Error in timetable - a finish event must be either the last or last but one for: " + TDEntry.HeadCode);
12203  TrainDataVector.clear();
12204  Utilities->CallLogPop(1829);
12205  return(false);
12206  }
12207  if(y == (TrainDataVector.at(x).ActionVector.size() - 2))
12208  {
12209  if(TrainDataVector.at(x).ActionVector.at(y + 1).FormatType != Repeat)
12210  {
12211  SecondPassMessage(GiveMessages, "Error in timetable - the only event that can follow a finish event is a repeat for: " +
12212  TDEntry.HeadCode);
12213  TrainDataVector.clear();
12214  Utilities->CallLogPop(1830);
12215  return(false);
12216  }
12217  }
12218  }
12219  }
12220  }
12221 
12222  // end of new preliminary checks
12223 
12224  // check ActionVector present and check start event successor validity
12225  // For Snt & Snt-sh set location if stopped, don't set any times yet
12226  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12227  {
12228  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12229  TActionVectorEntry & AVEntry0 = TrainDataVector.at(x).ActionVector.at(0);
12230  // use reference so can change internals where necessary
12231  if((AVEntry0.Command == "Snt") || (AVEntry0.Command == "Snt-sh"))
12232  {
12233  AnsiString LocationName = "";
12234  if(IsSNTEntryLocated(0, TDEntry, LocationName))
12235  // it is at a location
12236  {
12237  if(TDEntry.StartSpeed == 0) // stopped
12238  {
12239  AVEntry0.LocationName = LocationName;
12240  AVEntry0.LocationType = AtLocation;
12241  // check successor validity for located Snt that isn't a SignallerControl entry
12242  if(!AVEntry0.SignallerControl)
12243  {
12244  const TActionVectorEntry &AVEntry1 = TrainDataVector.at(x).ActionVector.at(1);
12245  // at least 2 entries present checked in integrity check so (1) valid
12246  if(!AtLocSuccessor(AVEntry1))
12247  {
12248  // Frh following Snt-sh will return false in location check, so no need to check here
12249  SecondPassMessage(GiveMessages, "Error in timetable - stopped 'Snt' or 'Snt-sh' followed by an illegal event for: " +
12250  TDEntry.HeadCode + ". The event isn't valid for a stationary train.");
12251  TrainDataVector.clear();
12252  Utilities->CallLogPop(523);
12253  return(false);
12254  }
12255  }
12256  }
12257  else
12258  {
12259  SecondPassMessage(GiveMessages, "Error in timetable - 'Snt' or 'Snt-sh' event at stop location but start speed not zero for: " +
12260  TDEntry.HeadCode);
12261  TrainDataVector.clear();
12262  Utilities->CallLogPop(791);
12263  return(false);
12264  }
12265  }
12266  else // check not Snt-sh & carry out successor validity checks for unlocated Snt that isn't a SignallerControl entry
12267  {
12268  if(AVEntry0.Command == "Snt-sh")
12269  {
12270  SecondPassMessage(GiveMessages, "Error in timetable - 'Snt-sh' event not at stop location for: " + TDEntry.HeadCode);
12271  TrainDataVector.clear();
12272  Utilities->CallLogPop(1042);
12273  return(false);
12274  }
12275  AVEntry0.LocationType = EnRoute;
12276  if(!AVEntry0.SignallerControl)
12277  {
12278  const TActionVectorEntry &AVEntry1 = TrainDataVector.at(x).ActionVector.at(1);
12279  // at least 2 entries checked in integrity check so (1) valid
12280  if(!MovingSuccessor(AVEntry1))
12281  {
12282  SecondPassMessage(GiveMessages, "Error in timetable - unlocated 'Snt' not followed by 'Fer', 'pas' or an arrival for: " +
12283  TDEntry.HeadCode);
12284  TrainDataVector.clear();
12285  Utilities->CallLogPop(790);
12286  return(false);
12287  }
12288  }
12289  }
12290  }
12291  // check other start successors
12292  else if(AVEntry0.SequenceType == Start)
12293  {
12294  const TActionVectorEntry &AVEntry1 = TrainDataVector.at(x).ActionVector.at(1);
12295  // at least 2 entries present checked in integrity check so (1) valid
12296  if(!AtLocSuccessor(AVEntry1))
12297  {
12298  SecondPassMessage(GiveMessages, "Error in timetable - 'Sfs', 'Sns', 'Sns-sh' or 'Sns-fsh' followed by an illegal event for: " +
12299  TDEntry.HeadCode + ". The event isn't valid for a stationary train.");
12300  TrainDataVector.clear();
12301  Utilities->CallLogPop(793);
12302  return(false);
12303  }
12304  }
12305  }
12306 
12307  // set Sfs, Sns, Sns-sh & 'Sns-fsh' locations same as following TimeLoc departure entry location, if no departure before end of sequence give error message
12308  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12309  {
12310  bool FoundFlag = false;
12311  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12312  TActionVectorEntry & AVEntry = TrainDataVector.at(x).ActionVector.at(0);
12313  // use reference so can change internals
12314  if((AVEntry.Command == "Sfs") || (AVEntry.Command == "Sns") || (AVEntry.Command == "Sns-sh") || (AVEntry.Command == "Sns-fsh"))
12315  {
12316  for(unsigned int y = 1; y < TrainDataVector.at(x).ActionVector.size(); y++)
12317  {
12318  const TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(y);
12319  if(AVEntry2.FormatType == TimeLoc)
12320  {
12321  FoundFlag = true;
12322  AVEntry.LocationName = AVEntry2.LocationName;
12323  break;
12324  }
12325  }
12326  if(!FoundFlag)
12327  {
12328  SecondPassMessage(GiveMessages, "Error in timetable - no location departure following an 'Sfs', 'Sns', 'Sns-sh'or 'Sns-fsh' event for: " +
12329  TDEntry.HeadCode);
12330  TrainDataVector.clear();
12331  Utilities->CallLogPop(851);
12332  return(false);
12333  }
12334  }
12335  }
12336 
12337  // set all cmd locations based on earlier location name in TimeLoc arrival or Sfs/Sns/Sns-sh/Sns-fsh/located Snt/Snt-sh locations
12338  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12339  {
12340  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12341  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12342  {
12343  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12344  if((AVEntry.FormatType == TimeLoc) || ((AVEntry.SequenceType == Start) && (AVEntry.LocationType == AtLocation)))
12345  {
12346  if(AVEntry.LocationName == "")
12347  // if TimeLoc turns out to be a TimeLoc departure then will emerge & be rejected in successor checks for TimeLocs
12348  {
12349  SecondPassMessage(GiveMessages, "Error in timetable for " + TDEntry.HeadCode +
12350  ": an event should have had a location name associated with it but it could not be found");
12351  TrainDataVector.clear();
12352  Utilities->CallLogPop(1831);
12353  return(false);
12354  // throw Exception("Error, entry location null in TimeLoc/Sfs/Sns/Sns-sh/Sns-fsh/Snt-sh/located Snt for Train: " + TDEntry.HeadCode);
12355  }
12356  for(unsigned int z = y + 1; z < TrainDataVector.at(x).ActionVector.size(); z++)
12357  {
12358  TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(z);
12359  // use reference so can change internals where necessary
12360  if((AVEntry2.Command != "") && (AVEntry2.LocationType == AtLocation))
12361  {
12362  AVEntry2.LocationName = AVEntry.LocationName;
12363  }
12364  else
12365  {
12366  break;
12367  }
12368  }
12369  }
12370  }
12371  }
12372  // all location names now set
12373 
12374  // check remaining successor validity except for TimeLoc arr & dep since those times not set yet
12375  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12376  {
12377  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12378  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12379  {
12380  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12381  if((AVEntry.SequenceType == Finish) && (AVEntry.Command != "F-nshs"))
12382  {
12383  if(y < (TrainDataVector.at(x).ActionVector.size() - 1))
12384  // i.e at least one more, must be a repeat
12385  {
12386  if(TrainDataVector.at(x).ActionVector.at(y + 1).FormatType != Repeat)
12387  {
12388  SecondPassMessage(GiveMessages, "Error in timetable - only a repeat can follow a finish entry for: " + TDEntry.HeadCode);
12389  TrainDataVector.clear();
12390  Utilities->CallLogPop(798);
12391  return(false);
12392  }
12393  }
12394  }
12395  if(AVEntry.Command == "F-nshs")
12396  {
12397  if(y != (TrainDataVector.at(x).ActionVector.size() - 1))
12398  // i.e has to be the last
12399  {
12400  SecondPassMessage(GiveMessages, "Error in timetable - F-nshs (shuttle link) must be the last event for: " + TDEntry.HeadCode);
12401  TrainDataVector.clear();
12402  Utilities->CallLogPop(1049);
12403  return(false);
12404  }
12405  }
12406  if(AVEntry.Command == "pas")
12407  {
12408  if(y >= (TrainDataVector.at(x).ActionVector.size() - 1))
12409  {
12410  SecondPassMessage(GiveMessages, "Error in timetable - a 'pas' can't be the last event for: " + TDEntry.HeadCode);
12411  TrainDataVector.clear();
12412  Utilities->CallLogPop(1518);
12413  return(false);
12414  }
12415  }
12416  if(AVEntry.Command == "jbo")
12417  {
12418  if(y >= (TrainDataVector.at(x).ActionVector.size() - 1))
12419  {
12420  SecondPassMessage(GiveMessages, "Error in timetable - a 'jbo' can't be the last event for: " + TDEntry.HeadCode);
12421  TrainDataVector.clear();
12422  Utilities->CallLogPop(800);
12423  return(false);
12424  }
12425  const TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(y + 1);
12426  if(!AtLocSuccessor(AVEntry2))
12427  {
12428  SecondPassMessage(GiveMessages, "Error in timetable - a jbo event is followed by an illegal event for: " + TDEntry.HeadCode +
12429  ". The event isn't valid for a stationary train.");
12430  TrainDataVector.clear();
12431  Utilities->CallLogPop(801);
12432  return(false);
12433  }
12434  }
12435  if((AVEntry.Command == "fsp") || (AVEntry.Command == "rsp"))
12436  {
12437  if(y >= (TrainDataVector.at(x).ActionVector.size() - 1))
12438  {
12439  SecondPassMessage(GiveMessages, "Error in timetable - a train split can't be the last event for: " + TDEntry.HeadCode);
12440  TrainDataVector.clear();
12441  Utilities->CallLogPop(802);
12442  return(false);
12443  }
12444  const TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(y + 1);
12445  if(!AtLocSuccessor(AVEntry2))
12446  {
12447  SecondPassMessage(GiveMessages, "Error in timetable - a train split is followed by an illegal event for: " + TDEntry.HeadCode +
12448  ". The event isn't valid for a stationary train.");
12449  TrainDataVector.clear();
12450  Utilities->CallLogPop(803);
12451  return(false);
12452  }
12453  }
12454  if(AVEntry.Command == "cdt")
12455  {
12456  if(y >= (TrainDataVector.at(x).ActionVector.size() - 1))
12457  {
12458  SecondPassMessage(GiveMessages, "Error in timetable - a 'cdt' can't be the last event for: " + TDEntry.HeadCode);
12459  TrainDataVector.clear();
12460  Utilities->CallLogPop(804);
12461  return(false);
12462  }
12463  const TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(y + 1);
12464  if(!AtLocSuccessor(AVEntry2))
12465  {
12466  SecondPassMessage(GiveMessages, "Error in timetable - a 'cdt' is followed by an illegal event for: " + TDEntry.HeadCode +
12467  ". The event isn't valid for a stationary train.");
12468  TrainDataVector.clear();
12469  Utilities->CallLogPop(805);
12470  return(false);
12471  }
12472  }
12473  if(AVEntry.FormatType == TimeTimeLoc)
12474  {
12475  if(y >= (TrainDataVector.at(x).ActionVector.size() - 1))
12476  {
12477  SecondPassMessage(GiveMessages, "Error in timetable - a timed arrival and departure can't be the last event for: " + TDEntry.HeadCode);
12478  TrainDataVector.clear();
12479  Utilities->CallLogPop(806);
12480  return(false);
12481  }
12482  const TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(y + 1);
12483  if(!MovingSuccessor(AVEntry2))
12484  {
12485  SecondPassMessage(GiveMessages, "Error in timetable - a timed arrival and departure is followed by an illegal event for: " +
12486  TDEntry.HeadCode + ". The event isn't valid for a moving train.");
12487  TrainDataVector.clear();
12488  Utilities->CallLogPop(807);
12489  return(false);
12490  }
12491  }
12492  if(AVEntry.FormatType == PassTime)
12493  {
12494  if(y >= (TrainDataVector.at(x).ActionVector.size() - 1))
12495  {
12496  SecondPassMessage(GiveMessages, "Error in timetable - a pass time can't be the last event for: " + TDEntry.HeadCode);
12497  TrainDataVector.clear();
12498  Utilities->CallLogPop(1530);
12499  return(false);
12500  }
12501  const TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(y + 1);
12502  if(!MovingSuccessor(AVEntry2))
12503  {
12504  SecondPassMessage(GiveMessages, "Error in timetable - a pass time is followed by an illegal event for: " + TDEntry.HeadCode +
12505  ". The event isn't valid for a moving train.");
12506  TrainDataVector.clear();
12507  Utilities->CallLogPop(1531);
12508  return(false);
12509  }
12510  }
12511  if(AVEntry.FormatType == Repeat)
12512  {
12513  if(y != (TrainDataVector.at(x).ActionVector.size() - 1))
12514  {
12515  SecondPassMessage(GiveMessages, "Error in timetable - a repeat is not the last item for: " + TDEntry.HeadCode);
12516  TrainDataVector.clear();
12517  Utilities->CallLogPop(808);
12518  return(false);
12519  }
12520  }
12521  }
12522  }
12523 
12524  // set arrival & departure times for TimeLocs & set their EventTimes to -1
12525  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12526  {
12527  bool LastEntryIsAnArrival = false;
12528  const TTrainDataEntry & TDEntry = TrainDataVector.at(x);
12529  // first deal with unlocated Snt entries - so next entry (TimeLoc or TimeTimeLoc) is an arrival, all else stopped so the next TimeLoc is a departure
12530  const TActionVectorEntry &AVEntry0 = TrainDataVector.at(x).ActionVector.at(0);
12531  if((AVEntry0.Command == "Snt") && (AVEntry0.LocationType == EnRoute))
12532  // StartSpeed may or may not be 0, but train will move forwards (if capable of doing so), & next TimeLoc will be an arrival, whether or not after one or more TimeTimeLocs
12533  {
12534  LastEntryIsAnArrival = false;
12535  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12536  {
12537  TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12538  if(AVEntry.FormatType == TimeLoc)
12539  {
12540  if((AVEntry.ArrivalTime > TDateTime(-1)) || (AVEntry.DepartureTime > TDateTime(-1)) || (AVEntry.EventTime == TDateTime(-1)))
12541  {
12542  throw Exception("Timetable error, TimeLoc times not as initially set for " + TDEntry.HeadCode);
12543  }
12544  if(LastEntryIsAnArrival)
12545  {
12546  AVEntry.DepartureTime = AVEntry.EventTime;
12547  AVEntry.EventTime = TDateTime(-1);
12548  LastEntryIsAnArrival = false;
12549  }
12550  else // last entry a departure
12551  {
12552  AVEntry.ArrivalTime = AVEntry.EventTime;
12553  AVEntry.EventTime = TDateTime(-1);
12554  LastEntryIsAnArrival = true;
12555  }
12556  }
12557  }
12558  }
12559  else // all others stopped at beginning
12560  {
12561  LastEntryIsAnArrival = true;
12562  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12563  {
12564  TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12565  if(AVEntry.FormatType == TimeLoc)
12566  {
12567  if((AVEntry.ArrivalTime > TDateTime(-1)) || (AVEntry.DepartureTime > TDateTime(-1)) || (AVEntry.EventTime == TDateTime(-1)))
12568  {
12569  throw Exception("Timetable error, TimeLoc times not as initially set for " + TDEntry.HeadCode);
12570  }
12571  if(LastEntryIsAnArrival)
12572  {
12573  AVEntry.DepartureTime = AVEntry.EventTime;
12574  AVEntry.EventTime = TDateTime(-1);
12575  LastEntryIsAnArrival = false;
12576  }
12577  else // last entry a departure
12578  {
12579  AVEntry.ArrivalTime = AVEntry.EventTime;
12580  AVEntry.EventTime = TDateTime(-1);
12581  LastEntryIsAnArrival = true;
12582  }
12583  }
12584  }
12585  }
12586  }
12587  // perform remaining successor checks for TimeLocs
12588  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12589  {
12590  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12591  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12592  {
12593  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12594  if((AVEntry.FormatType == TimeLoc) && (AVEntry.ArrivalTime >= TDateTime(0))) // arrival
12595  // TimeLoc (arr) -> No starts, repeats, Fer or TimeTimeLoc; TimeLoc (dep) or any other OK
12596  {
12597  if(y >= (TrainDataVector.at(x).ActionVector.size() - 1))
12598  {
12599  SecondPassMessage(GiveMessages, "Error in timetable - a timed arrival can't be the last event for: " + TDEntry.HeadCode);
12600  TrainDataVector.clear();
12601  Utilities->CallLogPop(809);
12602  return(false);
12603  }
12604  const TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(y + 1);
12605  if(!AtLocSuccessor(AVEntry2))
12606  {
12607  SecondPassMessage(GiveMessages, "Error in timetable - a timed arrival is followed by an illegal event for: " + TDEntry.HeadCode +
12608  ". The event isn't valid for a stationary train.");
12609  TrainDataVector.clear();
12610  Utilities->CallLogPop(810);
12611  return(false);
12612  }
12613  }
12614  if((AVEntry.FormatType == TimeLoc) && (AVEntry.DepartureTime >= TDateTime(0))) // departure
12615  // TimeLoc (dep) -> Fer, TimeLoc (arr), TimeTimeLoc, (new) pas OK, no others
12616  {
12617  if(y >= (TrainDataVector.at(x).ActionVector.size() - 1))
12618  {
12619  SecondPassMessage(GiveMessages, "Error in timetable - a timed departure can't be the last event for: " + TDEntry.HeadCode);
12620  TrainDataVector.clear();
12621  Utilities->CallLogPop(811);
12622  return(false);
12623  }
12624  const TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(y + 1);
12625  if(!MovingSuccessor(AVEntry2))
12626  {
12627  SecondPassMessage(GiveMessages, "Error in timetable - a timed departure is followed by an illegal event for: " + TDEntry.HeadCode +
12628  ". The event isn't valid for a moving train.");
12629  TrainDataVector.clear();
12630  Utilities->CallLogPop(812);
12631  return(false);
12632  }
12633  }
12634  }
12635  }
12636 
12637  // check all TimeLocs have either Arr or Dep time set and EventTime == -1, all Cmds have EventTime set & Arr & Dep times == -1,
12638  // & repeats have no times set
12639  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12640  {
12641  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12642  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12643  {
12644  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12645  if(AVEntry.FormatType == TimeLoc)
12646  {
12647  if(AVEntry.EventTime != TDateTime(-1))
12648  {
12649  throw Exception("Timetable error, TimeLoc entry has EventTime not -1 for " + TDEntry.HeadCode);
12650  }
12651  if((AVEntry.ArrivalTime == TDateTime(-1)) && (AVEntry.DepartureTime == TDateTime(-1)))
12652  {
12653  throw Exception("Timetable error, TimeLoc entry has neither arrival nor departure time set for " + TDEntry.HeadCode);
12654  }
12655  }
12656  if(AVEntry.FormatType == TimeTimeLoc)
12657  {
12658  if(AVEntry.EventTime != TDateTime(-1))
12659  {
12660  throw Exception("Timetable error, TimeTimeLoc entry has EventTime not -1 for " + TDEntry.HeadCode);
12661  }
12662  if((AVEntry.ArrivalTime == TDateTime(-1)) || (AVEntry.DepartureTime == TDateTime(-1)))
12663  {
12664  throw Exception("Timetable error, TimeTimeLoc entry has either arrival or departure time not set for " + TDEntry.HeadCode);
12665  }
12666  }
12667  if((AVEntry.FormatType == TimeCmd) || (AVEntry.FormatType == TimeCmdHeadCode) || (AVEntry.FormatType == StartNew) ||
12668  (AVEntry.FormatType == SNTShuttle) || (AVEntry.FormatType == SNSShuttle) || (AVEntry.FormatType == FNSNonRepeatToShuttle) ||
12669  (AVEntry.FormatType == FSHNewService) || (AVEntry.FormatType == PassTime))
12670  {
12671  if(AVEntry.EventTime == TDateTime(-1))
12672  {
12673  throw Exception("Timetable error, Cmd or PassTime entry has EventTime not set for " + TDEntry.HeadCode);
12674  }
12675  if((AVEntry.ArrivalTime != TDateTime(-1)) || (AVEntry.DepartureTime != TDateTime(-1)))
12676  {
12677  throw Exception("Timetable error, Cmd or PassTime entry has either arrival or departure time set for " + TDEntry.HeadCode);
12678  }
12679  }
12680  if(AVEntry.FormatType == Repeat)
12681  {
12682  if((AVEntry.EventTime != TDateTime(-1)) || (AVEntry.ArrivalTime != TDateTime(-1)) || (AVEntry.DepartureTime != TDateTime(-1)))
12683  {
12684  throw Exception("Timetable error, Repeat entry has a time set for " + TDEntry.HeadCode);
12685  }
12686  }
12687  }
12688  }
12689 
12690  // check times stay same or increase, note that can have time of 0 if include midnight
12691  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12692  {
12693  TDateTime CurrentTime = TTClockTime; // the timetable start time
12694  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12695  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12696  {
12697  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12698  if(AVEntry.FormatType == Repeat)
12699  {
12700  break;
12701  }
12702  if(AVEntry.FormatType == FinRemHere)
12703  {
12704  break;
12705  }
12706  if(AVEntry.FormatType == TimeTimeLoc)
12707  {
12708  if(AVEntry.DepartureTime < AVEntry.ArrivalTime)
12709  {
12710  SecondPassMessage(GiveMessages, "Error in timetable - a timed arrival and departure has a later arrival than departure time for: " +
12711  TDEntry.HeadCode);
12712  TrainDataVector.clear();
12713  Utilities->CallLogPop(813);
12714  return(false);
12715  }
12716  if(AVEntry.ArrivalTime < CurrentTime)
12717  {
12718  SecondPassMessage(GiveMessages, "Error in timetable - a timed arrival and departure has too early an arrival time for: " +
12719  TDEntry.HeadCode);
12720  TrainDataVector.clear();
12721  Utilities->CallLogPop(814);
12722  return(false);
12723  }
12724  CurrentTime = AVEntry.DepartureTime;
12725  continue;
12726  }
12727  if(AVEntry.FormatType == TimeLoc)
12728  {
12729  if(AVEntry.ArrivalTime >= TDateTime(0))
12730  {
12731  if(AVEntry.ArrivalTime < CurrentTime)
12732  {
12733  SecondPassMessage(GiveMessages, "Error in timetable - a timed location event has a time that is too early for: " + TDEntry.HeadCode);
12734  TrainDataVector.clear();
12735  Utilities->CallLogPop(815);
12736  return(false);
12737  }
12738  CurrentTime = AVEntry.ArrivalTime;
12739  }
12740  else
12741  {
12742  if(AVEntry.DepartureTime < CurrentTime)
12743  // both may be 0 legitimately so must allow for this
12744  {
12745  SecondPassMessage(GiveMessages, "Error in timetable - a timed location event has a time that is too early for: " + TDEntry.HeadCode);
12746  TrainDataVector.clear();
12747  Utilities->CallLogPop(816);
12748  return(false);
12749  }
12750  CurrentTime = AVEntry.DepartureTime;
12751  }
12752  continue;
12753  }
12754  if(AVEntry.EventTime < CurrentTime)
12755  // all others have EventTime set
12756  {
12757  SecondPassMessage(GiveMessages, "Error in timetable - a train event has a time that is set too early for: " + TDEntry.HeadCode +
12758  ", may be before timetable start time");
12759  TrainDataVector.clear();
12760  Utilities->CallLogPop(835);
12761  return(false);
12762  }
12763  CurrentTime = AVEntry.EventTime;
12764  continue;
12765  }
12766  }
12767 
12768  // check locations consistent
12769  AnsiString LastLocationName = "";
12770 
12771  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12772  {
12773  bool LastEntryIsAnArrival = false;
12774  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12775  // first deal with moving Snt entries (all else stopped)
12776  if((TrainDataVector.at(x).ActionVector.at(0).Command == "Snt") && (TrainDataVector.at(x).ActionVector.at(0).LocationType == EnRoute))
12777  {
12778  LastEntryIsAnArrival = false;
12779  LastLocationName = TrainDataVector.at(x).ActionVector.at(0).LocationName; // should be ""
12780  if(LastLocationName != "")
12781  {
12782  throw Exception("Timetable error, moving Snt entry has LocationName set for " + TDEntry.HeadCode);
12783  }
12784  for(unsigned int y = 1; y < TrainDataVector.at(x).ActionVector.size();
12785  y++) // note that immediate successor to a moving Snt can only be a Moving type
12786  {
12787  // if it's a SignallerControl entry then the condition isn't met
12788  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12789  if(AVEntry.FormatType == Repeat)
12790  {
12791  break; // repeat = reached end (+allows repeat after signaller controlled entry)
12792  }
12793  else if((AVEntry.FormatType == TimeCmdHeadCode) || (AVEntry.FormatType == FNSNonRepeatToShuttle))
12794  {
12795  if(AVEntry.LocationName != LastLocationName)
12796  {
12797  SecondPassMessage(GiveMessages, "Error in timetable - a location event is inconsistent for: " + TDEntry.HeadCode + " && " +
12798  AVEntry.Command);
12799  TrainDataVector.clear();
12800  Utilities->CallLogPop(823);
12801  return(false);
12802  }
12803  }
12804  else if(AVEntry.FormatType == TimeCmd)
12805  // cdt is the only TimeCmd
12806  {
12807  if(AVEntry.LocationName != LastLocationName)
12808  {
12809  SecondPassMessage(GiveMessages, "Error in timetable - a location event is inconsistent for: " + TDEntry.HeadCode + " && " +
12810  AVEntry.Command);
12811  TrainDataVector.clear();
12812  Utilities->CallLogPop(824);
12813  return(false);
12814  }
12815  }
12816  else if(AVEntry.FormatType == TimeTimeLoc)
12817  {
12818  if((AVEntry.LocationName == LastLocationName) && TTEditPanelVisible) //changed at v2.6.0 to allow loops & consecutive same locs
12819  // last entry must be a departure or would have failed earlier
12820  {
12821  TwoLocationList.insert(TwoLocationList.end(), TDEntry.ServiceReference); //added at v2.9.1
12822  TwoLocationFlag = true;
12823 // ShowMessage("Two or more locations are the same without a change of direction between them. Please correct if this is an error.\n\nThis warning will not be shown again.");
12824 // TwoOrMoreLocationsWarningGiven = true;
12825  }
12826  LastLocationName = AVEntry.LocationName;
12827  LastEntryIsAnArrival = false;
12828  }
12829  else if(AVEntry.FormatType == TimeLoc)
12830  {
12831  if(LastEntryIsAnArrival && (AVEntry.LocationName != LastLocationName))
12832  {
12833  SecondPassMessage(GiveMessages,
12834  "Error in timetable - a location event for a timed departure is different from the arrival location for: " + TDEntry.HeadCode);
12835  TrainDataVector.clear();
12836  Utilities->CallLogPop(826);
12837  return(false);
12838  }
12839  else if(!LastEntryIsAnArrival && (AVEntry.LocationName == LastLocationName))
12840  {
12841  SecondPassMessage(GiveMessages,
12842  "Error in timetable - a location event for a timed arrival is the same as the earlier departure location for: " + TDEntry.HeadCode);
12843  TrainDataVector.clear();
12844  Utilities->CallLogPop(827);
12845  return(false);
12846  }
12847  LastLocationName = AVEntry.LocationName;
12848  LastEntryIsAnArrival = !LastEntryIsAnArrival;
12849  }
12850  }
12851  }
12852  else // all stationary starting entries
12853  {
12854  LastEntryIsAnArrival = true;
12855  LastLocationName = TrainDataVector.at(x).ActionVector.at(0).LocationName;
12856  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12857  {
12858  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12859  if(AVEntry.FormatType == Repeat)
12860  {
12861  break;
12862  }
12863  else if((AVEntry.FormatType == TimeCmdHeadCode) || (AVEntry.FormatType == FNSNonRepeatToShuttle))
12864  // no need to add anything for shuttle starts since they are at loc (0) anyway
12865  {
12866  if(AVEntry.LocationName != LastLocationName)
12867  {
12868  SecondPassMessage(GiveMessages, "Error in timetable - a location event is inconsistent for: " + TDEntry.HeadCode + " && " +
12869  AVEntry.Command);
12870  TrainDataVector.clear();
12871  Utilities->CallLogPop(828);
12872  return(false);
12873  }
12874  }
12875  else if(AVEntry.FormatType == TimeCmd)
12876  // cdt is the only TimeCmd
12877  {
12878  if(AVEntry.LocationName != LastLocationName)
12879  {
12880  SecondPassMessage(GiveMessages, "Error in timetable - a location event is inconsistent for: " + TDEntry.HeadCode + " && " +
12881  AVEntry.Command);
12882  TrainDataVector.clear();
12883  Utilities->CallLogPop(829);
12884  return(false);
12885  }
12886  }
12887  else if(AVEntry.FormatType == TimeTimeLoc)
12888  {
12889  if((AVEntry.LocationName == LastLocationName) && TTEditPanelVisible) //changed at v2.6.0 to allow loops & consecutive same locs
12890  // last entry must be a departure or would have failed earlier
12891  {
12892  TwoLocationList.insert(TwoLocationList.end(), TDEntry.ServiceReference); //added at v2.9.1
12893  TwoLocationFlag = true;
12894 // ShowMessage("Two or more locations are the same without a change of direction between them. Please correct if this is an error.\n\nThis warning will not be shown again.");
12895 // TwoOrMoreLocationsWarningGiven = true;
12896  }
12897  LastLocationName = AVEntry.LocationName;
12898  LastEntryIsAnArrival = false;
12899  }
12900  else if(AVEntry.FormatType == TimeLoc)
12901  {
12902  if(LastEntryIsAnArrival && (AVEntry.LocationName != LastLocationName))
12903  {
12904  SecondPassMessage(GiveMessages,
12905  "Error in timetable - a location event for a timed departure is different from the arrival location for: " + TDEntry.HeadCode);
12906  TrainDataVector.clear();
12907  Utilities->CallLogPop(831);
12908  return(false);
12909  }
12910  if(!LastEntryIsAnArrival && (AVEntry.LocationName == LastLocationName) && !TwoOrMoreLocationsWarningGiven)
12911  {
12912  SecondPassMessage(GiveMessages,
12913  "A location event for a timed arrival is the same as the earlier departure location for: " + TDEntry.HeadCode + ". Please correct if this is an error.\n\nThis warning will not be shown again.");
12915 // TrainDataVector.clear();
12916 // Utilities->CallLogPop(832);
12917 // return false;
12918  }
12919  LastLocationName = AVEntry.LocationName;
12920  LastEntryIsAnArrival = !LastEntryIsAnArrival;
12921  }
12922  }
12923  }
12924  }
12925 
12926  // Check same location doesn't appear twice before a cdt except for separate arr & dep TimeLocs (just a potential error warning given in v2.6.0)
12927  // i.e. same location can appear in any number of consecutive entries but once changed couldn't repeat before a direction change prior to v2.6.0
12928  AnsiString LocationNameToBeChecked = "";
12929 
12930  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12931  {
12932  const TTrainDataEntry & TDEntry = TrainDataVector.at(x);
12933  unsigned int y = 0;
12934  const TActionVectorEntry &AVEntry0 = TDEntry.ActionVector.at(0);
12935  // first discard unlocated Snt entries as they don't have location name set
12936  if((AVEntry0.Command == "Snt") && (AVEntry0.LocationType == EnRoute))
12937  {
12938  y = 1;
12939  }
12940  while(y < TDEntry.ActionVector.size())
12941  // need to check each location name separately in turn, skipped for SignallerControl entries
12942  {
12943  if((TDEntry.ActionVector.at(y).Command == "Fer") || (TDEntry.ActionVector.at(y).FormatType == Repeat))
12944  {
12945  break; // out of the 'while' loop since have reached the end & 'Fer' & 'Repeat' have no location name set
12946  }
12947  LocationNameToBeChecked = TDEntry.ActionVector.at(y).LocationName;
12948  for(unsigned int z = y; z < TDEntry.ActionVector.size(); z++)
12949  {
12950  const TActionVectorEntry &AVEntry = TDEntry.ActionVector.at(z);
12951  if((AVEntry.Command == "Fer") || (AVEntry.FormatType == Repeat))
12952  {
12953  break; // out of the 'z' loop since have reached the end & 'Fer' & 'Repeat' have no location name set
12954  }
12955  if(AVEntry.Command == "cdt")
12956  {
12957  break; // out of the 'z' loop since the check is only valid up to a change of direction
12958  }
12959  if(AVEntry.LocationName == LocationNameToBeChecked)
12960  {
12961  continue; // keep going while name same
12962  }
12963  if(AVEntry.LocationName != LocationNameToBeChecked)
12964  // if name different check forwards to see if repeats
12965  {
12966  for(unsigned int a = z; a < TDEntry.ActionVector.size(); a++)
12967  {
12968  if(TDEntry.ActionVector.at(a).Command == "cdt")
12969  {
12970  break; // out of the 'a' & 'z' loops since the check is only valid up to a change of direction
12971  }
12972  if((TDEntry.ActionVector.at(a).LocationName == LocationNameToBeChecked) && TTEditPanelVisible) //changed at v2.6.0 to allow loops & consecutive same locs
12973  {
12974  TwoLocationList.insert(TwoLocationList.end(), TDEntry.ServiceReference); //added at v2.9.1
12975  TwoLocationFlag = true;
12976 // ShowMessage("Two or more locations are the same without a change of direction between them. Please correct if this is an error.\n\nThis warning will not be shown again.");
12977 // TwoOrMoreLocationsWarningGiven = true;
12978  }
12979  }
12980  break; // out of the 'z' loop since have checked 'a' as far as need to
12981  }
12982  }
12983  y++;
12984  }
12985  }
12986  if(TwoLocationFlag)
12987  {
12988  TwoLocationList.sort(); //need to sort first in alphabetical order to ensure all duplictes removed
12989  TwoLocationList.unique(); //remove duplicates
12990  }
12991 
12992  // check all locations except unlocated 'Snt' & 'Fer' have LocationName set
12993  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12994  {
12995  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12996  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12997  {
12998  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12999  if((AVEntry.LocationName == "") && (AVEntry.Command != "Snt") && (AVEntry.Command != "Fer") && (AVEntry.FormatType != Repeat))
13000  {
13001  throw Exception("Error, non- 'Snt', 'Fer' or Repeat entry doesn't have a location name set for " + TDEntry.HeadCode);
13002  }
13003  AnsiString LocName = "";
13004  // dummy, only used so can call IsSNTEntryLocated
13005  if((AVEntry.Command == "Snt") && (IsSNTEntryLocated(1, TrainDataVector.at(x), LocName)))
13006  {
13007  if(AVEntry.LocationName == "")
13008  {
13009  throw Exception("Error, 'Snt' entry at a stop location doesn't have a location name set for " + TDEntry.HeadCode);
13010  }
13011  }
13012  if((AVEntry.Command == "Snt") && !(IsSNTEntryLocated(2, TrainDataVector.at(x), LocName)))
13013  {
13014  if(AVEntry.LocationName != "")
13015  {
13016  throw Exception("Error, 'Snt' unlocated entry has a location name set for " + TDEntry.HeadCode);
13017  }
13018  }
13019  }
13020  }
13021 
13022 /* Check a split to 'x' doesn't again split to 'x' (anywhere, not just for one train, since headcodes can be duplicated)
13023  Check each joined by train not joined by same train again (anywhere, not just for one train, since headcodes can be duplicated)
13024  Check each change of headcode not repeated anywhere else (anywhere, not just for one train, since headcodes can be duplicated)
13025 
13026  i.e. check everywhere where there is an 'OtherHeadCode' that it matches once only with its reference (both ways) + set
13027  the OtherHeadCodeStartingEntryPtr pointers where appropriate + train information for splits & new services
13028 
13029  BUT need to separate the shuttles from non-shuttles, because can have two trains reference each other in both forms,
13030  eg 2F44 Sns-sh ends in Fns to 2F45, & Sns 2F45 ends in Fns-sh to 2F44. Here 2F45 is the 'OtherHeadCode' for both
13031  Sns-sh & Fns in train 2F44, & 2F44 is the 'OtherHeadCode' for both Sns & Fns-sh in train 2F45.
13032 */
13033  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // new test to ensure no duplicate links at all, other checks ensure none for shuttles,
13034  {
13035  // non-shuttles & non-repeating links separately, but don't check that there isn't a
13036  // duplicate between a non-repeating shuttle and another - leave original tests in as
13037  // these also set the pointers
13038  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13039  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13040  {
13041  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13042  if(AVEntry.OtherHeadCode != "")
13043  {
13044  if(!CheckForDuplicateCrossReferences(0, TDEntry.HeadCode, AVEntry.OtherHeadCode, GiveMessages))
13045  {
13046  Utilities->CallLogPop(1584);
13047  return(false); // error message given in called function
13048  }
13049  }
13050  if(AVEntry.NonRepeatingShuttleLinkHeadCode != "")
13051  {
13052  if(!CheckForDuplicateCrossReferences(1, TDEntry.HeadCode, AVEntry.NonRepeatingShuttleLinkHeadCode, GiveMessages))
13053  {
13054  Utilities->CallLogPop(1585);
13055  return(false); // error message given in called function
13056  }
13057  }
13058  }
13059  }
13060 
13061  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13062  {
13063  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13064  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13065  {
13066  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13067  if((AVEntry.Command != "Sns-sh") && (AVEntry.Command != "Snt-sh") && (AVEntry.Command != "Fns-sh") && (AVEntry.Command != "Frh-sh"))
13068  {
13069  if(AVEntry.OtherHeadCode != "")
13070  {
13071  if(!CheckCrossReferencesAndSetData(0, TDEntry.HeadCode, AVEntry.OtherHeadCode, false, GiveMessages))
13072  // false = non-shuttle
13073  {
13074  Utilities->CallLogPop(864);
13075  return(false); // error message given in called function
13076  }
13077  }
13078  }
13079  }
13080  }
13081 
13082  // now repeat the check just for the shuttles
13083  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13084  {
13085  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13086  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13087  {
13088  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13089  if((AVEntry.Command == "Sns-sh") || (AVEntry.Command == "Snt-sh") || (AVEntry.Command == "Fns-sh") || (AVEntry.Command == "Frh-sh"))
13090  {
13091  if(AVEntry.OtherHeadCode != "")
13092  {
13093  if(!CheckCrossReferencesAndSetData(1, TDEntry.HeadCode, AVEntry.OtherHeadCode, true, GiveMessages))
13094  // true = shuttle
13095  {
13096  Utilities->CallLogPop(1100);
13097  return(false); // error message given in called function
13098  }
13099  }
13100  }
13101  }
13102  }
13103 
13104  // check for proper non-repeating link cross references and that they have no repeats & that times are consistent
13105  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13106  {
13107  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13108  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13109  {
13110  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13111  if(AVEntry.NonRepeatingShuttleLinkHeadCode != "")
13112  {
13114  {
13115  Utilities->CallLogPop(1060);
13116  return(false); // error message given in called function
13117  }
13118  }
13119  }
13120  }
13121 
13122  // check that each shuttle start ends either in Fns or Fxx-sh (though a single service can't end in Fxx-sh), and that
13123  // when the Fxx-sh is reached it references the original start and not another shuttle - not allowed to link two shuttles,
13124  // don't ever need to and as designed would skip repeats
13125  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13126  {
13127  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13128  {
13129  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13130  if((AVEntry.Command == "Sns-sh") || (AVEntry.Command == "Snt-sh"))
13131  {
13132  if(!CheckShuttleServiceIntegrity(0, &(TrainDataVector.at(x)), GiveMessages))
13133  {
13134  Utilities->CallLogPop(1090);
13135  return(false); // error message given in called function
13136  }
13137  }
13138  }
13139  }
13140 
13141  // check all entries have all types set to something
13142  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13143  {
13144  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13145  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13146  {
13147  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13148  if(AVEntry.FormatType == NoFormat)
13149  {
13150  throw Exception("Error - timetable ActionVector entry no. " + AnsiString(y) + " has FormatType unset for: " + TDEntry.HeadCode);
13151  }
13152  else if(AVEntry.SequenceType == NoSequence)
13153  {
13154  throw Exception("Error - timetable ActionVector entry no. " + AnsiString(y) + " has SequenceType unset for: " + TDEntry.HeadCode);
13155  }
13156  else if(AVEntry.LocationType == NoLocation)
13157  {
13158  throw Exception("Error - timetable ActionVector entry no. " + AnsiString(y) + " has LocationType unset for: " + TDEntry.HeadCode);
13159  }
13160  else if(AVEntry.ShuttleLinkType == NoShuttleLink)
13161  {
13162  throw Exception("Error - timetable ActionVector entry no. " + AnsiString(y) + " has ShuttleLinkType unset for: " + TDEntry.HeadCode);
13163  }
13164  }
13165  }
13166 
13167  // all OK if reach here, so set up the TrainOperatingDataVector (already has one entry) & NumberOfTrains
13168  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13169  {
13170  TTrainDataEntry & TDEntry = TrainDataVector.at(x);
13171  // non-const reference so can alter content
13172  TTrainOperatingData TData;
13173  const TActionVectorEntry &LastAVEntry = TDEntry.ActionVector.at(TDEntry.ActionVector.size() - 1);
13174  if(LastAVEntry.FormatType == Repeat) // check if a repeat
13175  {
13176 /*
13177  class TTrainOperatingData
13178  {
13179  public:
13180  int TrainID; - default, set at construction
13181  TActionEventType EventReported; used during operation
13182  TRunningEntry RunningEntry; - default, set at construction
13183  TTrainOperatingData() {TrainID = -1; EventReported= NoEvent; RunningEntry=NotStarted;} //constructor, values set to defaults
13184  };
13185 */
13186  TDEntry.NumberOfTrains = LastAVEntry.NumberOfRepeats + 1;
13187  for(int y = 1; y < TDEntry.NumberOfTrains; y++)
13188  {
13189  TDEntry.TrainOperatingDataVector.push_back(TData);
13190  }
13191  }
13192  else
13193  {
13194  TDEntry.NumberOfTrains = 1;
13195  }
13196  }
13197 
13198  // check that don't include any Continuation names
13199  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13200  {
13201  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13202  {
13203  AnsiString LocName = TrainDataVector.at(x).ActionVector.at(y).LocationName;
13204  AnsiString HC = TrainDataVector.at(x).HeadCode;
13205  if(LocName != "")
13206  {
13207  if(Track->ContinuationNameMap.find(LocName) != Track->ContinuationNameMap.end())
13208  {
13209  SecondPassMessage(GiveMessages, "Error in timetable - continuation names (" + LocName + ") must not be included, see service " + HC);
13210  TrainDataVector.clear();
13211  Utilities->CallLogPop(1578);
13212  return(false);
13213  }
13214  }
13215  }
13216  }
13217 
13218  // check that all repeat times below 96h
13219  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13220  {
13221  int NumRepeats = (TrainDataVector.at(x).NumberOfTrains) - 1;
13222  int IncMinutes = 0;
13223  if(TrainDataVector.at(x).ActionVector.at(TrainDataVector.at(x).ActionVector.size() - 1).FormatType == Repeat)
13224  {
13225  IncMinutes = TrainDataVector.at(x).ActionVector.at(TrainDataVector.at(x).ActionVector.size() - 1).RearStartOrRepeatMins;
13226  }
13227  else
13228  {
13229  continue; // basic times already checked in CheckTimeValidity
13230  }
13231  AnsiString HC = TrainDataVector.at(x).HeadCode;
13232  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13233  {
13234  if((double)TrainDataVector.at(x).ActionVector.at(y).EventTime > -1)
13235  {
13236  if(((double)GetRepeatTime(32, TrainDataVector.at(x).ActionVector.at(y).EventTime, NumRepeats, IncMinutes) >= 3.9994))
13237  {
13238  SecondPassMessage(GiveMessages, "Error in timetable - a repeat time exceeds 95h 59m, see service " + HC); // 3d 23h 59m = 3.9993055556
13239  TrainDataVector.clear();
13240  Utilities->CallLogPop(1818);
13241  return(false);
13242  }
13243  }
13244  if((double)TrainDataVector.at(x).ActionVector.at(y).ArrivalTime > -1)
13245  {
13246  if(((double)GetRepeatTime(33, TrainDataVector.at(x).ActionVector.at(y).EventTime, NumRepeats, IncMinutes) >= 3.9994))
13247  // 3d 23h 59m = 3.9993055556
13248  {
13249  SecondPassMessage(GiveMessages, "Error in timetable - a repeat entry time exceeds 95h 59m, see service " + HC);
13250  TrainDataVector.clear();
13251  Utilities->CallLogPop(1819);
13252  return(false);
13253  }
13254  }
13255  if((double)TrainDataVector.at(x).ActionVector.at(y).DepartureTime > -1)
13256  {
13257  if(((double)GetRepeatTime(34, TrainDataVector.at(x).ActionVector.at(y).EventTime, NumRepeats, IncMinutes) >= 3.9994))
13258  // 3d 23h 59m = 3.9993055556
13259  {
13260  SecondPassMessage(GiveMessages, "Error in timetable - a repeat entry time exceeds 95h 59m, see service " + HC);
13261  TrainDataVector.clear();
13262  Utilities->CallLogPop(1820);
13263  return(false);
13264  }
13265  }
13266  }
13267  }
13268 
13269  // Now that all set up change any extended headcodes back to ordinary headcodes
13270  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13271  {
13272  StripExcessFromHeadCode(0, TrainDataVector.at(x).HeadCode);
13273  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13274  {
13275  StripExcessFromHeadCode(1, TrainDataVector.at(x).ActionVector.at(y).OtherHeadCode);
13276  StripExcessFromHeadCode(2, TrainDataVector.at(x).ActionVector.at(y).NonRepeatingShuttleLinkHeadCode);
13277  }
13278  }
13279 
13280  // SaveTrainDataVectorToFile(0);//test
13282  Utilities->CallLogPop(782);
13283  return(true);
13284 }
13285 
13286 // ---------------------------------------------------------------------------
13287 // Moving successors: TimeLoc arr/TimeTimeLoc/pas/Fer;
13289 {
13290  return ((AVEntry.FormatType == TimeLoc) || (AVEntry.FormatType == TimeTimeLoc) || (AVEntry.Command == "pas") || (AVEntry.Command == "Fer"));
13291 }
13292 
13293 // ---------------------------------------------------------------------------
13294 // AtLoc successors: TimeLoc dep/jbo/fsp/rsp/cdt/Frh/Fns/Fjo/Frh-sh/Fns-sh/F-nshs;
13296 {
13297  return ((AVEntry.FormatType == TimeLoc) || (AVEntry.Command == "jbo") || (AVEntry.Command == "fsp") || (AVEntry.Command == "rsp") ||
13298  (AVEntry.Command == "cdt") || (AVEntry.Command == "Frh") || (AVEntry.Command == "Fns") || (AVEntry.Command == "Fjo") || (AVEntry.Command == "Frh-sh") ||
13299  (AVEntry.Command == "Fns-sh") || (AVEntry.Command == "F-nshs"));
13300 }
13301 
13302 // ---------------------------------------------------------------------------
13303 
13304 void TTrainController::StripExcessFromHeadCode(int Caller, AnsiString &HeadCode)
13305 {
13306  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",StripExcessFromHeadCode," + HeadCode);
13307  if(HeadCode.Length() > 4) // ignore otherwise
13308  {
13309  HeadCode = HeadCode.SubString(HeadCode.Length() - 3, 4);
13310  }
13311  Utilities->CallLogPop(1593);
13312 }
13313 
13314 // ---------------------------------------------------------------------------
13315 
13316 bool TTrainController::CheckForDuplicateCrossReferences(int Caller, AnsiString MainHeadCode, AnsiString SecondHeadCode, bool GiveMessages)
13317 {
13318  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckForDuplicateCrossReferences," + MainHeadCode + "," +
13319  SecondHeadCode);
13320  int ForwardCount = 0;
13321  int ReverseCount = 0;
13322 
13323  if(MainHeadCode == SecondHeadCode)
13324  {
13325  SecondPassMessage(GiveMessages, "Error in timetable - Service " + MainHeadCode + " has an event that references itself");
13326  TrainDataVector.clear();
13327  Utilities->CallLogPop(1594);
13328  return(false);
13329  }
13330  // forward check
13331  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13332  {
13333  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13334  if(TDEntry.HeadCode == MainHeadCode)
13335  {
13336  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13337  {
13338  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13339  if(AVEntry.OtherHeadCode == SecondHeadCode)
13340  {
13341  ForwardCount++;
13342  }
13343  if(AVEntry.NonRepeatingShuttleLinkHeadCode == SecondHeadCode)
13344  // need own check in case both 'Other' & 'NonRepeating' have same headcode
13345  {
13346  ForwardCount++;
13347  }
13348  }
13349  }
13350  }
13351  if(ForwardCount == 0)
13352  // this is an exception because the headcodes are selected in the same order as the forward check
13353  {
13354  throw Exception("Error, ForwardCount == 0 in CheckForDuplicateCrossReferences after called with found values");
13355  }
13356  if(ForwardCount > 2)
13357  // can have 2 if one is Sns-sh linking from another leg of the shuttle, and Fns links out to that same leg
13358  {
13359  SecondPassMessage(GiveMessages, "Error in timetable - found more than two references to " + SecondHeadCode + " from a train whose headcode is " +
13360  MainHeadCode + ". Check the service cross references from each service, and check whether one or other service is listed twice or more.");
13361  TrainDataVector.clear();
13362  Utilities->CallLogPop(1587);
13363  return(false);
13364  }
13365  // reverse check
13366  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13367  {
13368  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13369  if(TDEntry.HeadCode == SecondHeadCode)
13370  {
13371  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13372  {
13373  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13374  if(AVEntry.OtherHeadCode == MainHeadCode)
13375  {
13376  ReverseCount++;
13377  }
13378  if(AVEntry.NonRepeatingShuttleLinkHeadCode == MainHeadCode)
13379  {
13380  ReverseCount++;
13381  }
13382  }
13383  }
13384  }
13385 
13386  if(ReverseCount == 0)
13387  {
13388  SecondPassMessage(GiveMessages, "Error in timetable - cross reference missing in either " + MainHeadCode + " or " + SecondHeadCode);
13389  TrainDataVector.clear();
13390  Utilities->CallLogPop(1588);
13391  return(false);
13392  }
13393  if(ReverseCount > 2)
13394  // can have 2 if one is a second shuttle leg with a link in from Fns, and it links out to the same service with Fxx-sh
13395  {
13396  SecondPassMessage(GiveMessages, "Error in timetable - found more than two references to " + MainHeadCode + " from a train whose headcode is " +
13397  SecondHeadCode + ". Check the service cross references from each service, and check whether one or other service is listed twice or more.");
13398  TrainDataVector.clear();
13399  Utilities->CallLogPop(1589);
13400  return(false);
13401  }
13402  if(ForwardCount != ReverseCount)
13403  {
13404  SecondPassMessage(GiveMessages, "Error in timetable - " + MainHeadCode + " has a different number of references to " + SecondHeadCode +
13405  " than the other way round");
13406  TrainDataVector.clear();
13407  Utilities->CallLogPop(1610);
13408  return(false);
13409  }
13410  Utilities->CallLogPop(1590);
13411  return(true);
13412 }
13413 
13414 // ---------------------------------------------------------------------------
13415 
13416 bool TTrainController::CheckCrossReferencesAndSetData(int Caller, AnsiString MainHeadCode, AnsiString OtherHeadCode, bool Shuttle, bool GiveMessages)
13417 /* Return false for no find or more than one find, check correct types of link
13418  First run through all trains whose headcode is the MainHeadCode (may be > 1) & for each entry whose
13419  'other' is OtherHeadCode increment a forward counter. Keep a pointer to the 'OtherHeadCode' entry for use later
13420  Must be exactly 1 forward count. NB Forward relates to MainHeadCode
13421  Then do the same in reverse.
13422  Using the pointers check the event times, then check that the locations & commands match - if main is a split then other must be Sfs;
13423  if main is Fns other must be Sns; if main is jbo other must be Fjo.
13424  Also check platform lengths OK for a split location (call to Track function for this - at least one platform at location has to be long
13425  enough). If all succeeds so far set the relevant OtherHeadCodeStartingEntryPtr to the new service starting point + train information
13426  for Sfs & Sns services. Finally check the repeat entries if present are consistent
13427 
13428  Check all except the NonRepeatingShuttleLinkHeadCodes, which only occur from F-nshs to Sns-sh, and from Fns-sh to
13429  Sns-fsh. All others should check out OK, but check shuttles & non-shuttles separately.
13430 
13431  /NB prohibit main & other headcodes being same, causes probs in failing to recognise locations
13432 */
13433 
13434 {
13435  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckCrossReferencesAndSetData," + MainHeadCode + "," + OtherHeadCode);
13436  int ForwardCount = 0;
13437  int ReverseCount = 0;
13438  unsigned int ForwardTDVectorNumber, ReverseTDVectorNumber;
13439  TActionVectorEntry *ReverseEntryPtr = 0, *ForwardEntryPtr = 0;
13440  TTrainDataEntry *MainTrainDataPtr = 0;
13441  TTrainDataEntry *OtherTrainDataPtr = 0;
13442 
13443  // forward check
13444  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13445  {
13446  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13447  if(TDEntry.HeadCode == MainHeadCode)
13448  {
13449  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13450  {
13451  TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13452  if(!Shuttle && (AVEntry.Command != "Sns-sh") && (AVEntry.Command != "Snt-sh") && (AVEntry.Command != "Fns-sh") && (AVEntry.Command != "Frh-sh"))
13453  {
13454  if(AVEntry.OtherHeadCode == OtherHeadCode)
13455  {
13456  MainTrainDataPtr = &TrainDataVector.at(x);
13457  ForwardEntryPtr = &AVEntry;
13458  ForwardCount++;
13459  ForwardTDVectorNumber = x;
13460  }
13461  }
13462  else if(Shuttle && ((AVEntry.Command == "Sns-sh") || (AVEntry.Command == "Snt-sh") || (AVEntry.Command == "Fns-sh") ||
13463  (AVEntry.Command == "Frh-sh")))
13464  {
13465  if(AVEntry.OtherHeadCode == OtherHeadCode)
13466  {
13467  MainTrainDataPtr = &TrainDataVector.at(x);
13468  ForwardEntryPtr = &AVEntry;
13469  ForwardCount++;
13470  ForwardTDVectorNumber = x;
13471  }
13472  }
13473  }
13474  }
13475  }
13476  if(ForwardCount == 0)
13477  // this is an exception because the headcodes are selected in the same order as the forward check
13478  {
13479  throw Exception("Error, ForwardCount == 0 in CheckCrossReferencesAndSetData after called with found values");
13480  }
13481  if(ForwardCount > 1)
13482  {
13483  SecondPassMessage(GiveMessages, "Error in timetable - found more than one reference to " + OtherHeadCode + " from a train whose headcode is " +
13484  MainHeadCode);
13485  TrainDataVector.clear();
13486  Utilities->CallLogPop(836);
13487  return(false);
13488  }
13489  // reverse check
13490  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13491  {
13492  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13493  if(TDEntry.HeadCode == OtherHeadCode)
13494  {
13495  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13496  {
13497  TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13498  if(!Shuttle && (AVEntry.Command != "Sns-sh") && (AVEntry.Command != "Snt-sh") && (AVEntry.Command != "Fns-sh") && (AVEntry.Command != "Frh-sh"))
13499  {
13500  if(AVEntry.OtherHeadCode == MainHeadCode)
13501  {
13502  OtherTrainDataPtr = &TrainDataVector.at(x);
13503  ReverseCount++;
13504  ReverseEntryPtr = &AVEntry;
13505  ReverseTDVectorNumber = x;
13506  }
13507  }
13508  else if(Shuttle && ((AVEntry.Command == "Sns-sh") || (AVEntry.Command == "Snt-sh") || (AVEntry.Command == "Fns-sh") || (AVEntry.Command == "Frh-sh")))
13509  {
13510  if(AVEntry.OtherHeadCode == MainHeadCode)
13511  {
13512  OtherTrainDataPtr = &TrainDataVector.at(x);
13513  ReverseCount++;
13514  ReverseEntryPtr = &AVEntry;
13515  ReverseTDVectorNumber = x;
13516  }
13517  }
13518  }
13519  }
13520  }
13521 
13522  if(ReverseCount == 0)
13523  {
13524  SecondPassMessage(GiveMessages, "Error in timetable - cross reference missing in either " + MainHeadCode + " or " + OtherHeadCode);
13525  TrainDataVector.clear();
13526  Utilities->CallLogPop(837);
13527  return(false);
13528  }
13529  if(ReverseCount > 1)
13530  {
13531  SecondPassMessage(GiveMessages, "Error in timetable - found more than one reference to " + MainHeadCode + " from a train whose headcode is " +
13532  OtherHeadCode);
13533  TrainDataVector.clear();
13534  Utilities->CallLogPop(838);
13535  return(false);
13536  }
13537  // these will all be false for !Shuttle
13538  bool ForwardShuttleStart = ((ForwardEntryPtr->Command == "Sns-sh") || (ForwardEntryPtr->Command == "Snt-sh"));
13539  bool ForwardShuttleFinish = ((ForwardEntryPtr->Command == "Fns-sh") || (ForwardEntryPtr->Command == "Frh-sh"));
13540  bool ReverseShuttleStart = ((ReverseEntryPtr->Command == "Sns-sh") || (ReverseEntryPtr->Command == "Snt-sh"));
13541  bool ReverseShuttleFinish = ((ReverseEntryPtr->Command == "Fns-sh") || (ReverseEntryPtr->Command == "Frh-sh"));
13542 
13543  if(Shuttle && MainTrainDataPtr->ActionVector.back().FormatType != Repeat)
13544  {
13545  SecondPassMessage(GiveMessages, "Error in timetable - shuttle train " + MainHeadCode + " does not have a repeat");
13546  TrainDataVector.clear();
13547  Utilities->CallLogPop(1058);
13548  return(false);
13549  }
13550  if(Shuttle && OtherTrainDataPtr->ActionVector.back().FormatType != Repeat)
13551  {
13552  SecondPassMessage(GiveMessages, "Error in timetable - shuttle train " + OtherHeadCode + " does not have a repeat");
13553  TrainDataVector.clear();
13554  Utilities->CallLogPop(1059);
13555  return(false);
13556  }
13557  if(ForwardEntryPtr->LocationName == "")
13558  {
13559  SecondPassMessage(GiveMessages, "Error in timetable - location error in cross referenced trains " + MainHeadCode + " and " + OtherHeadCode +
13560  ". One or other service does not have a location set");
13561  TrainDataVector.clear();
13562  Utilities->CallLogPop(526);
13563  return(false);
13564  }
13565  if(ReverseEntryPtr->LocationName == "")
13566  {
13567  SecondPassMessage(GiveMessages, "Error in timetable - location error in cross referenced trains " + MainHeadCode + " and " + OtherHeadCode +
13568  ". One or other service does not have a location set");
13569  TrainDataVector.clear();
13570  Utilities->CallLogPop(527);
13571  return(false);
13572  }
13573  if(ForwardEntryPtr->LocationName != ReverseEntryPtr->LocationName)
13574  {
13575  SecondPassMessage(GiveMessages, "Error in timetable - cross referenced train " + OtherHeadCode +
13576  " is at a different location to the referencing train " + MainHeadCode);
13577  TrainDataVector.clear();
13578  Utilities->CallLogPop(842);
13579  return(false);
13580  }
13581  // ignore shuttle repeat links for first time check
13582  if(!Shuttle)
13583  {
13584  if(ForwardEntryPtr->EventTime != ReverseEntryPtr->EventTime)
13585  {
13586  SecondPassMessage(GiveMessages, "Error in timetable - cross referenced train " + OtherHeadCode +
13587  " has a different event time to the referencing train " + MainHeadCode);
13588  TrainDataVector.clear();
13589  Utilities->CallLogPop(525);
13590  return(false);
13591  }
13592  }
13593  // need to allow for repeat times multiplying up by repeating time for shuttle repeat links
13594  // no need to check from reverse to forward as already checked links consistent, and if include will send message twice
13595  if(ForwardShuttleStart && ReverseShuttleFinish)
13596  // Shuttle must be true if these are true
13597  {
13598  if(!CheckShuttleRepeatTime(0, ForwardEntryPtr->EventTime, ReverseEntryPtr->EventTime, OtherTrainDataPtr->ActionVector.back().RearStartOrRepeatMins))
13599  {
13600  SecondPassMessage(GiveMessages, "Error in timetable - shuttle service " + MainHeadCode +
13601  " first repeat restart time not consistent with finish service " + OtherHeadCode);
13602  TrainDataVector.clear();
13603  Utilities->CallLogPop(1055);
13604  return(false);
13605  }
13606  }
13607  if((ReverseEntryPtr->Command == "Sfs") || (ReverseEntryPtr->Command == "Sns"))
13608  // doesn't matter about ForwardEntryPtr being Sfs/Sns as called for every occurrence of an 'OtherHeadCode' so won't escape
13609  {
13610  if(ReverseTDVectorNumber == ForwardTDVectorNumber)
13611  {
13612  SecondPassMessage(GiveMessages, "Error in timetable - an 'Sfs' or 'Sns' event (" + OtherHeadCode +
13613  ") appears in the same sequence as the corresponding linked event " + MainHeadCode);
13614  TrainDataVector.clear();
13615  Utilities->CallLogPop(528);
13616  return(false);
13617  }
13618  }
13619  if(ReverseEntryPtr->Command == "Fjo")
13620  // doesn't matter about ForwardEntryPtr being Fjo as called for every occurrence of an 'OtherHeadCode' so won't escape
13621  {
13622  if(ReverseTDVectorNumber == ForwardTDVectorNumber)
13623  {
13624  SecondPassMessage(GiveMessages, "Error in timetable - an 'Fjo' event (" + OtherHeadCode +
13625  ") appears in the same sequence as the corresponding linked event " + MainHeadCode);
13626  TrainDataVector.clear();
13627  Utilities->CallLogPop(862);
13628  return(false);
13629  }
13630  }
13631  if(ReverseEntryPtr->Command == "Fns")
13632  // doesn't matter about ForwardEntryPtr being Fns as called for every occurrence of an 'OtherHeadCode' so won't escape
13633  {
13634  if(ReverseTDVectorNumber == ForwardTDVectorNumber)
13635  {
13636  SecondPassMessage(GiveMessages, "Error in timetable - an 'Fns' event (" + OtherHeadCode +
13637  ") appears in the same sequence as the corresponding linked event " + MainHeadCode);
13638  TrainDataVector.clear();
13639  Utilities->CallLogPop(529);
13640  return(false);
13641  }
13642  }
13643  if(ForwardEntryPtr->Command == "Sfs")
13644  {
13645  if((ReverseEntryPtr->Command != "fsp") && (ReverseEntryPtr->Command != "rsp"))
13646  {
13647  SecondPassMessage(GiveMessages,
13648  "Error in timetable - unable to find a corresponding split train event for the train that starts from a split whose headcode is " +
13649  MainHeadCode);
13650  TrainDataVector.clear();
13651  Utilities->CallLogPop(530);
13652  return(false);
13653  }
13654  }
13655  if((ForwardEntryPtr->Command == "fsp") || (ForwardEntryPtr->Command == "rsp"))
13656  {
13657  if(ReverseEntryPtr->Command != "Sfs")
13658  {
13659  SecondPassMessage(GiveMessages, "Error in timetable - unable to find a corresponding 'Sfs' event for the train split whose headcode is " +
13660  MainHeadCode);
13661  TrainDataVector.clear();
13662  Utilities->CallLogPop(839);
13663  return(false);
13664  }
13665  else
13666  {
13667  if(!(Track->TimetabledLocationNameAllocated(4, ForwardEntryPtr->LocationName)))
13668  {
13669  SecondPassMessage(GiveMessages, "Error in timetable - can't find timetabled location '" + ForwardEntryPtr->LocationName + "' in railway - perhaps there are concourses without platforms?");
13670  TrainDataVector.clear();
13671  Utilities->CallLogPop(849);
13672  return(false);
13673  }
13674  if(!(Track->OneNamedLocationElementAtLocation(0, ForwardEntryPtr->LocationName)))
13675  {
13676  SecondPassMessage(GiveMessages, "Error in timetable - can't find any named location elements at '" + ForwardEntryPtr->LocationName + "' - perhaps there are concourses without platforms?");
13677  TrainDataVector.clear();
13678  Utilities->CallLogPop(850);
13679  return(false);
13680  }
13681  if(!(Track->OneNamedLocationLongEnoughForSplit(0, ForwardEntryPtr->LocationName)))
13682  {
13683  SecondPassMessage(GiveMessages, "Error in timetable - location too short to split a train at " + ForwardEntryPtr->LocationName);
13684  TrainDataVector.clear();
13685  Utilities->CallLogPop(846);
13686  return(false);
13687  }
13688  ForwardEntryPtr->LinkedTrainEntryPtr = OtherTrainDataPtr;
13689  ReverseEntryPtr->LinkedTrainEntryPtr = MainTrainDataPtr;
13690  if(OtherTrainDataPtr->Description == "")
13691  {
13692  OtherTrainDataPtr->Description = MainTrainDataPtr->Description;
13693  }
13694  // NB: May not be set if main train is a service continuation without a description, if so can't do much about it but doesn't affect operation, just the train information display
13695  OtherTrainDataPtr->MaxRunningSpeed = MainTrainDataPtr->MaxRunningSpeed;
13696  // Probably redundant as this is continued from the earlier service when the changeover happens (also may not be set here yet if a service continuation)
13697  }
13698  }
13699  if(ForwardEntryPtr->Command == "Sns")
13700  {
13701  if(ReverseEntryPtr->Command != "Fns")
13702  {
13703  SecondPassMessage(GiveMessages, "Error in timetable - unable to find a corresponding 'Fns' event for the 'Sns' train whose headcode is " +
13704  MainHeadCode + " and is formed from a service with headcode " + OtherHeadCode);
13705  TrainDataVector.clear();
13706  Utilities->CallLogPop(531);
13707  return(false);
13708  }
13709  }
13710  if(ForwardEntryPtr->Command == "Fns")
13711  {
13712  if(ReverseEntryPtr->Command != "Sns")
13713  {
13714  SecondPassMessage(GiveMessages, "Error in timetable - unable to find a corresponding 'Sns' event for the train whose headcode is " + MainHeadCode +
13715  " and forms a new service with headcode " + OtherHeadCode);
13716  TrainDataVector.clear();
13717  Utilities->CallLogPop(840);
13718  return(false);
13719  }
13720  else
13721  {
13722  ForwardEntryPtr->LinkedTrainEntryPtr = OtherTrainDataPtr;
13723  ReverseEntryPtr->LinkedTrainEntryPtr = MainTrainDataPtr;
13724  if(OtherTrainDataPtr->Description == "")
13725  {
13726  OtherTrainDataPtr->Description = MainTrainDataPtr->Description;
13727  }
13728  // Probably redundant as this is continued from the earlier service when the changeover happens (also may not be set here yet if a service continuation)
13729  OtherTrainDataPtr->MaxRunningSpeed = MainTrainDataPtr->MaxRunningSpeed;
13730  // Probably redundant as this is continued from the earlier service when the changeover happens (also may not be set here yet if a service continuation)
13731  }
13732  }
13733  if(ForwardEntryPtr->Command == "jbo")
13734  {
13735  if(ReverseEntryPtr->Command != "Fjo")
13736  {
13737  SecondPassMessage(GiveMessages, "Error in timetable - unable to find a corresponding 'Fjo' event for the train whose headcode is " + MainHeadCode +
13738  " and is joined by a train with headcode " + OtherHeadCode);
13739  TrainDataVector.clear();
13740  Utilities->CallLogPop(841);
13741  return(false);
13742  }
13743  }
13744  if(ForwardEntryPtr->Command == "Fjo")
13745  {
13746  if(ReverseEntryPtr->Command != "jbo")
13747  {
13748  SecondPassMessage(GiveMessages, "Error in timetable - unable to find a corresponding 'jbo' event for the train whose headcode is " + MainHeadCode +
13749  " and joins a train with headcode " + OtherHeadCode);
13750  TrainDataVector.clear();
13751  Utilities->CallLogPop(532);
13752  return(false);
13753  }
13754  else
13755  {
13756  ForwardEntryPtr->LinkedTrainEntryPtr = OtherTrainDataPtr;
13757  ReverseEntryPtr->LinkedTrainEntryPtr = MainTrainDataPtr;
13758  if((MainTrainDataPtr->MaxRunningSpeed > 5) && (MainTrainDataPtr->MaxRunningSpeed < OtherTrainDataPtr->MaxRunningSpeed))
13759  {
13760  OtherTrainDataPtr->MaxRunningSpeed = MainTrainDataPtr->MaxRunningSpeed;
13761  }
13762  // added test for > 5 [5 used instead of 0 because of possible floating point errors - though unlikely] above at v1.3.1 because the train will have a zero MaxRunningSpeed if it continues from another service - its max speed is set when it takes over from the other service
13763  // notified of this problem by Ian Walker in his email of 25/03/13. Probably redundant anyway because the max speed is reduced at the changeover if the 'joined by' train's max speed is less.
13764  }
13765  }
13766  if(ForwardShuttleStart)
13767  // (ForwardEntryPtr->Command == "Sns-sh") || (ForwardEntryPtr->Command == "Snt-sh"))
13768  {
13769  if(!ReverseShuttleFinish)
13770  // (ReverseEntryPtr->Command != "Fns-sh") && (ReverseEntryPtr->Command != "Frh-sh"))
13771  {
13772  SecondPassMessage(GiveMessages, "Error in timetable - incorrect shuttle link to train whose headcode is " + MainHeadCode +
13773  " from train whose headcode is " + OtherHeadCode + ", has to be Fns-sh, Frh-sh");
13774  TrainDataVector.clear();
13775  Utilities->CallLogPop(1056);
13776  return(false);
13777  }
13778  }
13779  if(ReverseShuttleStart)
13780  // (ReverseEntryPtr->Command == "Sns-sh") || (ReverseEntryPtr->Command == "Snt-sh"))
13781  {
13782  if(!ForwardShuttleFinish)
13783  // (ForwardEntryPtr->Command != "Fns-sh") && (ForwardEntryPtr->Command != "Frh-sh"))
13784  {
13785  SecondPassMessage(GiveMessages, "Error in timetable - incorrect shuttle link to train whose headcode is " + OtherHeadCode +
13786  " from train whose headcode is " + MainHeadCode + ", has to be Fns-sh, Frh-sh");
13787  TrainDataVector.clear();
13788  Utilities->CallLogPop(1057);
13789  return(false);
13790  }
13791  else
13792  {
13793  ForwardEntryPtr->LinkedTrainEntryPtr = OtherTrainDataPtr;
13794  ReverseEntryPtr->LinkedTrainEntryPtr = MainTrainDataPtr;
13795 /* don't need LinkedTrainEntryPtr for 'OtherTrain' & don't need data transfer as this is done in the
13796  non-repeating link for Sns-sh & is provided at the outset for Snt-sh
13797 */
13798  }
13799  }
13800  // check repeat information consistent if present
13801  // note that won't be affected by the non-repeating shuttle links as these are in NonRepeatingShuttleLinkHeadCode
13802  // and those not accessed here
13803 
13804  // still need to check the non-repeating links and that they have no repeats - do that outside this function
13805  bool MainRepeat = false, OtherRepeat = false;
13806  TActionVectorEntry MainRepeatEntry, OtherRepeatEntry;
13807 
13808  if(MainTrainDataPtr->ActionVector.at(MainTrainDataPtr->ActionVector.size() - 1).FormatType == Repeat)
13809  {
13810  MainRepeat = true;
13811  MainRepeatEntry = MainTrainDataPtr->ActionVector.at(MainTrainDataPtr->ActionVector.size() - 1);
13812  }
13813  if(OtherTrainDataPtr->ActionVector.at(OtherTrainDataPtr->ActionVector.size() - 1).FormatType == Repeat)
13814  {
13815  OtherRepeat = true;
13816  OtherRepeatEntry = OtherTrainDataPtr->ActionVector.at(OtherTrainDataPtr->ActionVector.size() - 1);
13817  }
13818  if((MainRepeat && !OtherRepeat) || (!MainRepeat && OtherRepeat))
13819  {
13820  SecondPassMessage(GiveMessages, "Error in timetable - only one repeat is provided for the train whose headcode is " + MainHeadCode +
13821  " and the associated train with headcode " + OtherHeadCode);
13822  TrainDataVector.clear();
13823  Utilities->CallLogPop(844);
13824  return(false);
13825  }
13826  if(MainRepeat && OtherRepeat)
13827  {
13828  if((MainRepeatEntry.EventTime != OtherRepeatEntry.EventTime) || (MainRepeatEntry.RearStartOrRepeatMins != OtherRepeatEntry.RearStartOrRepeatMins) ||
13829  (MainRepeatEntry.FrontStartOrRepeatDigits != OtherRepeatEntry.FrontStartOrRepeatDigits) ||
13830  (MainRepeatEntry.NumberOfRepeats != OtherRepeatEntry.NumberOfRepeats))
13831  {
13832  SecondPassMessage(GiveMessages, "Error in timetable - repeat items don't correspond for the train whose headcode is " + MainHeadCode +
13833  " and the associated train with headcode " + OtherHeadCode);
13834  TrainDataVector.clear();
13835  Utilities->CallLogPop(845);
13836  return(false);
13837  }
13838  }
13839  Utilities->CallLogPop(863);
13840  return(true);
13841 }
13842 
13843 // ---------------------------------------------------------------------------
13844 
13845 void TTrainController::StripSpaces(int Caller, AnsiString &Input)
13846 // strip both leading and trailing spaces at ends of text and spaces before and after all commas and semicolons within the text
13847 {
13848  // strip spaces from extreme ends of input
13849  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",StripSpaces," + AnsiString(Input));
13850  if(Input == "")
13851  {
13852  Utilities->CallLogPop(856);
13853  return;
13854  }
13855  while(Input[1] == ' ')
13856  {
13857  if(Input.Length() > 1)
13858  {
13859  Input = Input.SubString(2, Input.Length() - 1);
13860  }
13861  else
13862  {
13863  Input = "";
13864  Utilities->CallLogPop(857);
13865  return;
13866  }
13867  }
13868  if(Input == "")
13869  {
13870  Utilities->CallLogPop(858);
13871  return;
13872  }
13873  while(Input[Input.Length()] == ' ')
13874  {
13875  if(Input.Length() > 1)
13876  {
13877  Input = Input.SubString(1, Input.Length() - 1);
13878  }
13879  else
13880  {
13881  Input = "";
13882  Utilities->CallLogPop(859);
13883  return;
13884  }
13885  }
13886  // now strip spaces immediately after all commas and semicolons within the text
13887  AnsiString Output = "";
13888  bool DelimiterFound = false;
13889 
13890  for(int x = 1; x < Input.Length() + 1; x++)
13891  {
13892  if(DelimiterFound)
13893  {
13894  if(Input[x] == ' ')
13895  {
13896  continue;
13897  }
13898  }
13899  if((Input[x] != ',') && (Input[x] != ';'))
13900  {
13901  DelimiterFound = false;
13902  Output = Output + Input[x];
13903  }
13904  else
13905  {
13906  DelimiterFound = true;
13907  Output = Output + Input[x];
13908  }
13909  }
13910  if(Output == "")
13911  {
13912  Input = "";
13913  Utilities->CallLogPop(860);
13914  return;
13915  }
13916  // now strip spaces immediately before all commas and semicolons within the text
13917  Input = Output;
13918  Output = "";
13919  DelimiterFound = false;
13920  for(int x = Input.Length(); x > 0; x--)
13921  {
13922  if(DelimiterFound)
13923  {
13924  if(Input[x] == ' ')
13925  {
13926  continue;
13927  }
13928  }
13929  if((Input[x] != ',') && (Input[x] != ';'))
13930  {
13931  DelimiterFound = false;
13932  Output = AnsiString(Input[x]) + Output;
13933  }
13934  else
13935  {
13936  DelimiterFound = true;
13937  Output = AnsiString(Input[x]) + Output;
13938  }
13939  }
13940  Input = Output;
13941  Utilities->CallLogPop(861);
13942 }
13943 
13944 // ---------------------------------------------------------------------------
13945 
13946 bool TTrainController::IsSNTEntryLocated(int Caller, const TTrainDataEntry &TDEntry, AnsiString &LocationName)
13947 // checks if an Snt or Snt-sh entry with zero starting speed is followed (somewhere, not necessarily immediately) by a TimeLoc & has the same LocationName
13948 // and if so returns true. Also returns true for Snt, not Snt-sh, if at least 1 start element is a location & the entry is either
13949 // a signaller control entry & speed is zero or it is followed immediately by Frh or Fjo (mod at v2.0.0 for empty stock pickup).
13950 // Always return false for entry at a continuation (may be named but not a stop location). Note that no successor validity checks
13951 // are done in this function, they must be done elsewhere.
13952 //a starting speed > 0 always returns false
13953 {
13954  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",IsSNTEntryLocated," + AnsiString(TDEntry.HeadCode));
13955  const TActionVectorEntry &AVEntry0 = TDEntry.ActionVector.at(0);
13956  LocationName = "";
13957  if(TDEntry.StartSpeed > 0)
13958  {
13959  Utilities->CallLogPop(1784);
13960  return(false);
13961  }
13962  if((AVEntry0.Command != "Snt") && (AVEntry0.Command != "Snt-sh"))
13963  {
13964  throw Exception("Error, first entry not 'Snt' or 'Snt-sh' in IsSNTEntryLocated");
13965  }
13967  {
13968  Utilities->CallLogPop(852);
13969  return(false);
13970  }
13971  AnsiString LocRear = Track->TrackElementAt(507, AVEntry0.RearStartOrRepeatMins).ActiveTrackElementName;
13972  AnsiString LocFront = Track->TrackElementAt(508, AVEntry0.FrontStartOrRepeatDigits).ActiveTrackElementName;
13973 
13974  if(LocRear != "")
13975  {
13976  LocationName = LocRear;
13977  }
13978  else
13979  {
13980  LocationName = LocFront;
13981  }
13982  if(LocationName == "")
13983  {
13984  Utilities->CallLogPop(1036);
13985  return(false);
13986  }
13987  if(AVEntry0.SignallerControl)
13988  {
13989  Utilities->CallLogPop(1773);
13990  return(true);
13991  }
13992 // here if not a signaller start entry so must be at least one more entry, and it is at a location
13993 
13994 //Ok Not ok continue
13995 
13996 //Frh if Snt Frh-sh cdt
13997 //Fns if Snt Fns-sh fsp or rsp
13998 //Fjo if Snt TimeTimeLoc jbo
13999 //F-nshs if Snt pas
14000 //TimeLoc dep Fer
14001 // TimeLoc arr
14002 
14003  for(unsigned int y = 0; y < TDEntry.ActionVector.size(); y++)
14004  {
14005  const TActionVectorEntry &AVEntry = TDEntry.ActionVector.at(y);
14006  if(((AVEntry.Command == "Frh") || (AVEntry.Command == "Fjo") || (AVEntry.Command == "F-nshs") || (AVEntry.Command == "Fns")) && (AVEntry0.Command == "Snt")) // added Fjo at v2.0.0 for empty stock
14007  {
14008  Utilities->CallLogPop(1037);
14009  return(true);
14010  }
14011  if((AVEntry.FormatType == TimeLoc) && (AVEntry.LocationName == LocationName)) //will be a departure if same name- times not set yet so can't use them to confirm
14012  {
14013  Utilities->CallLogPop(2442);
14014  return(true);
14015  }
14016  if((AVEntry.FormatType == TimeLoc) && (AVEntry.LocationName != LocationName)) //arrival, not located
14017  {
14018  Utilities->CallLogPop(2438);
14019  return(false);
14020  }
14021  if((AVEntry.Command == "Fer") || (AVEntry.Command == "pas") || (AVEntry.Command == "Fns-sh") || (AVEntry.Command == "Frh-sh") || (AVEntry.FormatType == TimeTimeLoc))
14022  {
14023  Utilities->CallLogPop(854);
14024  return(false);
14025  }
14026  if((AVEntry.Command == "cdt") || (AVEntry.Command == "fsp") || (AVEntry.Command == "rsp") || (AVEntry.Command == "jbo"))
14027  {
14028  continue;
14029  }
14030  }
14031  Utilities->CallLogPop(855);
14032  return(false);
14033 
14034 }
14035 
14036 // ---------------------------------------------------------------------------
14037 
14038 bool TTrainController::CheckStartPositionValidity(int Caller, AnsiString RearElementStr, AnsiString FrontElementStr, bool GiveMessages)
14039 {
14040  // checks that the new train start elements are valid - both exist & are connected, and that not
14041  // attempting to start on a diverging leg (i.e. one segment on points & other on element connected to diverging leg)
14042  // & not starting with front on a continuation
14043  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckStartPositionValidity," + RearElementStr + "," + FrontElementStr);
14044  int RearPosition = 0, FrontPosition = 0, RearExitPos = 0;
14045 
14046  RearPosition = Track->GetTrackVectorPositionFromString(5, RearElementStr, GiveMessages);
14047  if(RearPosition < 0)
14048  // error message given in GetTrackVectorPositionFromString
14049  {
14050  Utilities->CallLogPop(759);
14051  return(false);
14052  }
14053  FrontPosition = Track->GetTrackVectorPositionFromString(6, FrontElementStr, GiveMessages);
14054  if(FrontPosition < 0)
14055  // error message given in GetTrackVectorPositionFromString
14056  {
14057  Utilities->CallLogPop(760);
14058  return(false);
14059  }
14060  TTrackElement RearTrackElement = Track->TrackElementAt(490, RearPosition);
14061  TTrackElement FrontTrackElement = Track->TrackElementAt(491, FrontPosition);
14062  TTrackType RearType = RearTrackElement.TrackType, FrontType = FrontTrackElement.TrackType;
14063 
14064  // check front & rear connected
14065  for(int x = 0; x < 4; x++)
14066  {
14067  if(RearTrackElement.Conn[x] == FrontPosition)
14068  {
14069  RearExitPos = x;
14070  break;
14071  }
14072  if(x == 3)
14073  {
14074  TimetableMessage(GiveMessages, "Front element: " + FrontTrackElement.ElementID + " not linked to rear element: " + RearTrackElement.ElementID);
14075  Utilities->CallLogPop(762);
14076  return(false);
14077  }
14078  }
14079  // check not starting with front on a continuation
14080  if(FrontType == Continuation)
14081  {
14082  TimetableMessage(GiveMessages, "Front of train attempting to start on a continuation at: " + FrontElementStr);
14083  Utilities->CallLogPop(937);
14084  return(false);
14085  }
14086  // check not starting on a level crossing
14087  if(Track->IsLCAtHV(43, FrontTrackElement.HLoc, FrontTrackElement.VLoc))
14088  {
14089  TimetableMessage(GiveMessages, "Train attempting to start on a level crossing at: " + FrontElementStr);
14090  Utilities->CallLogPop(1951);
14091  return(false);
14092  }
14093  if(Track->IsLCAtHV(44, RearTrackElement.HLoc, RearTrackElement.VLoc))
14094  {
14095  TimetableMessage(GiveMessages, "Train attempting to start on a level crossing at: " + RearElementStr);
14096  Utilities->CallLogPop(1952);
14097  return(false);
14098  }
14099  // check if trying to start on diverging leg of points
14100  if((RearType == Points) && (RearExitPos == 3))
14101  {
14102  TimetableMessage(GiveMessages, "Front of train attempting to start on element connected to diverging points at: " + RearElementStr);
14103  Utilities->CallLogPop(936);
14104  return(false);
14105  }
14106  if((FrontType == Points) && (RearTrackElement.ConnLinkPos[RearExitPos] == 3))
14107  {
14108  TimetableMessage(GiveMessages, "Rear of train attempting to start on element connected to diverging points at: " + FrontElementStr);
14109  Utilities->CallLogPop(1808);
14110  return(false);
14111  }
14112  Utilities->CallLogPop(905);
14113  return(true);
14114 }
14115 
14116 // ---------------------------------------------------------------------------
14117 
14118 bool TTrainController::CheckStartAllowable(int Caller, int RearPosition, int RearExitPos, AnsiString HeadCode, bool ReportFlag, TActionEventType &EventType)
14119 // Rear & front element validity already checked in CheckStartPositionValidity
14120 // This checks for points in correct orientation, no train at start position and not starting on a locked route
14121 {
14122  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckStartAllowable," + AnsiString(RearPosition) + "," +
14123  AnsiString(RearExitPos));
14124  TTrackElement RearTrackElement = Track->TrackElementAt(517, RearPosition);
14125 
14126  if(RearTrackElement.TrackType == Continuation)
14127  {
14128  EventType = FailTrainEntry;
14129  }
14130  else
14131  {
14132  EventType = FailCreateTrain;
14133  }
14134  int FrontPosition = RearTrackElement.Conn[RearExitPos];
14135  TTrackElement FrontTrackElement = Track->TrackElementAt(798, FrontPosition);
14136  int FrontEntryPos = RearTrackElement.ConnLinkPos[RearExitPos];
14137  TTrackType RearType = RearTrackElement.TrackType;
14138  TTrackType FrontType = FrontTrackElement.TrackType;
14139  AnsiString RearName, FrontName;
14140 
14141  if(RearTrackElement.ActiveTrackElementName != "")
14142  {
14143  RearName = RearTrackElement.ActiveTrackElementName;
14144  }
14145  else
14146  {
14147  RearName = RearTrackElement.ElementID;
14148  }
14149  if(FrontTrackElement.ActiveTrackElementName != "")
14150  {
14151  FrontName = FrontTrackElement.ActiveTrackElementName;
14152  }
14153  else
14154  {
14155  FrontName = FrontTrackElement.ElementID;
14156  }
14157  TPrefDirElement PrefDirElement; // needed for next function but not used
14158  int LockedVectorNumber; // needed for next function but not used
14159 
14160  if(AllRoutes->IsElementInLockedRouteGetPrefDirElementGetLockedVectorNumber(12, FrontPosition, FrontEntryPos, PrefDirElement, LockedVectorNumber))
14161  {
14162  if(ReportFlag)
14163  {
14164  if(EventType == FailCreateTrain)
14165  {
14166  EventType = FailCreateLockedRoute;
14167  }
14168  else
14169  {
14170  EventType = FailEnterLockedRoute;
14171  }
14172  LogActionError(47, HeadCode, "", EventType, FrontName);
14173  }
14174  Utilities->CallLogPop(940);
14175  return(false);
14176  }
14177  if(AllRoutes->IsElementInLockedRouteGetPrefDirElementGetLockedVectorNumber(13, RearPosition, RearExitPos, PrefDirElement, LockedVectorNumber))
14178  {
14179  if(ReportFlag)
14180  {
14181  if(EventType == FailCreateTrain)
14182  {
14183  EventType = FailCreateLockedRoute;
14184  }
14185  else
14186  {
14187  EventType = FailEnterLockedRoute;
14188  }
14189  LogActionError(48, HeadCode, "", EventType, RearName);
14190  }
14191  Utilities->CallLogPop(1809);
14192  return(false);
14193  }
14194  if((RearType != Bridge) && (RearTrackElement.TrainIDOnElement > -1))
14195  {
14196  if(ReportFlag)
14197  {
14198  LogActionError(27, HeadCode, "", EventType, RearName);
14199  }
14200  Utilities->CallLogPop(1810);
14201  return(false);
14202  }
14203  if((FrontType != Bridge) && (FrontTrackElement.TrainIDOnElement > -1))
14204  {
14205  if(ReportFlag)
14206  {
14207  if(EventType == FailCreateTrain)
14208  {
14209  LogActionError(28, HeadCode, "", EventType, FrontName);
14210  }
14211  else
14212  {
14213  LogActionError(43, HeadCode, "", EventType, RearName);
14214  }
14215  }
14216  Utilities->CallLogPop(941);
14217  return(false);
14218  }
14219  if(RearType == Bridge)
14220  {
14221  if((RearExitPos > 1) && (RearTrackElement.TrainIDOnBridgeTrackPos23 > -1))
14222  {
14223  if(ReportFlag)
14224  {
14225  LogActionError(29, HeadCode, "", EventType, RearName);
14226  }
14227  Utilities->CallLogPop(942);
14228  return(false);
14229  }
14230  if((RearExitPos < 2) && (RearTrackElement.TrainIDOnBridgeTrackPos01 > -1))
14231  {
14232  if(ReportFlag)
14233  {
14234  LogActionError(30, HeadCode, "", EventType, RearName);
14235  }
14236  Utilities->CallLogPop(943);
14237  return(false);
14238  }
14239  }
14240  if(FrontType == Bridge)
14241  {
14242  if((FrontEntryPos > 1) && (FrontTrackElement.TrainIDOnBridgeTrackPos23 > -1))
14243  {
14244  if(ReportFlag)
14245  {
14246  if(EventType == FailCreateTrain)
14247  {
14248  LogActionError(31, HeadCode, "", EventType, FrontName);
14249  }
14250  else
14251  {
14252  LogActionError(44, HeadCode, "", EventType, RearName);
14253  }
14254  }
14255  Utilities->CallLogPop(944);
14256  return(false);
14257  }
14258  if((FrontEntryPos < 2) && (FrontTrackElement.TrainIDOnBridgeTrackPos01 > -1))
14259  {
14260  if(ReportFlag)
14261  {
14262  if(EventType == FailCreateTrain)
14263  {
14264  LogActionError(45, HeadCode, "", EventType, FrontName);
14265  }
14266  else
14267  {
14268  LogActionError(46, HeadCode, "", EventType, RearName);
14269  }
14270  }
14271  Utilities->CallLogPop(945);
14272  return(false);
14273  }
14274  }
14275  EventType = FailCreatePoints;
14276  if(RearType == Points)
14277  {
14278  if(RearTrackElement.Attribute == 1)
14279  {
14280  if(ReportFlag)
14281  {
14282  LogActionError(33, HeadCode, "", FailCreatePoints, RearName);
14283  }
14284  Utilities->CallLogPop(933);
14285  return(false);
14286  }
14287  }
14288  if(FrontType == Points)
14289  {
14290  if(FrontTrackElement.Attribute == 1)
14291  {
14292  if(ReportFlag)
14293  {
14294  LogActionError(34, HeadCode, "", FailCreatePoints, FrontName);
14295  }
14296  Utilities->CallLogPop(934);
14297  return(false);
14298  }
14299  }
14300 
14301  //this section added at v2.9.1 to prevent entry for a train when there's a route set against it
14302  int HLoc = Track->TrackElementAt(1027, RearPosition).HLoc;
14303  int VLoc = Track->TrackElementAt(1028, RearPosition).VLoc;
14304  int ELink = Track->TrackElementAt(1029, RearPosition).Link[RearExitPos]; //if route entry corresponds to RearExitPos then it's set against the train
14305  int RouteNumber; //not used
14306  if(Track->TrackElementAt(1030, RearPosition).TrackType == Continuation)
14307  {
14308  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(8, HLoc, VLoc, ELink, RouteNumber))
14309  {
14310  EventType = FailEntryRouteSetAgainst;
14311  if(ReportFlag)
14312  {
14313  LogActionError(63, HeadCode, "", EventType, RearName);
14314  }
14315  Utilities->CallLogPop(2317);
14316  return(false);
14317  }
14318  }
14319  Utilities->CallLogPop(939);
14320  return(true);
14321 }
14322 
14323 // ---------------------------------------------------------------------------
14324 
14325 AnsiString TTrainController::GetRepeatHeadCode(int Caller, AnsiString BaseHeadCode, int RepeatNumber, int IncDigits)
14326 {
14327  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetRepeatHeadCode," + BaseHeadCode + "," + AnsiString(RepeatNumber) +
14328  "," + AnsiString(IncDigits));
14329  if(!Last2CharactersBothDigits(1, BaseHeadCode) && (IncDigits > 0))
14330  {
14331  throw Exception("Error, last 2 characters not both digits and IncDigits > 0 in GetRepeatHeadCode");
14332  }
14333  if(!Last2CharactersBothDigits(2, BaseHeadCode))
14334  {
14335  Utilities->CallLogPop(1893);
14336  return(BaseHeadCode);
14337  }
14338  int BaseDigits = BaseHeadCode.SubString(3, 2).ToInt();
14339  int NextRepeatDigits = BaseDigits + (IncDigits * RepeatNumber);
14340 
14341  while(NextRepeatDigits >= 100)
14342  {
14343  NextRepeatDigits -= 100; // rolls over after 99
14344  }
14345  AnsiString NextRepeatDigitsStr = AnsiString(NextRepeatDigits);
14346 
14347  if(NextRepeatDigitsStr.Length() < 2)
14348  {
14349  NextRepeatDigitsStr = AnsiString('0') + NextRepeatDigitsStr;
14350  }
14351  AnsiString NextRepeatHeadCode = BaseHeadCode.SubString(1, 2) + NextRepeatDigitsStr;
14352 
14353  Utilities->CallLogPop(1365);
14354  return(NextRepeatHeadCode);
14355 }
14356 
14357 // ---------------------------------------------------------------------------
14358 
14359 TDateTime TTrainController::GetRepeatTime(int Caller, TDateTime BasicTime, int RepeatNumber, int IncMinutes)
14360 {
14361  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetRepeatTime," + AnsiString(double(BasicTime)) + "," +
14362  AnsiString(RepeatNumber) + "," + AnsiString(IncMinutes));
14363  TDateTime NextRepeatTime = BasicTime + TDateTime(((double)(RepeatNumber * IncMinutes)) / 1440.0); // 1440 = no. of minutes in 24h
14364  Utilities->CallLogPop(1366);
14365  return(NextRepeatTime);
14366 }
14367 
14368 // ---------------------------------------------------------------------------
14369 
14370 bool TTrainController::CheckShuttleRepeatTime(int Caller, TDateTime ForwardEventTime, TDateTime ReverseEventTime, int RepeatMinutes)
14371 // For success the ForwardEventTime + repeat time should == ReverseEventTime (allow 10secs either way since converting to doubles)
14372 {
14373  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckShuttleRepeatTime," + AnsiString(double(ForwardEventTime)) + "," +
14374  AnsiString(double(ReverseEventTime)) + "," + AnsiString(RepeatMinutes));
14375  int ForwardSecs = int(double(ForwardEventTime) * 86400);
14376  int ReverseSecs = int(double(ReverseEventTime) * 86400);
14377  int RepeatSecs = RepeatMinutes * 60;
14378 
14379  if((ForwardSecs > (ReverseSecs - RepeatSecs + 10)) || (ForwardSecs < (ReverseSecs - RepeatSecs - 10)))
14380  {
14381  Utilities->CallLogPop(1367);
14382  return(false);
14383  }
14384  else
14385  {
14386  Utilities->CallLogPop(1368);
14387  return(true);
14388  }
14389 }
14390 
14391 // ---------------------------------------------------------------------------
14392 
14393 bool TTrainController::CheckNonRepeatingShuttleLinksAndSetData(int Caller, AnsiString MainHeadCode, AnsiString NonRepeatingHeadCode, bool GiveMessages)
14394 // check for proper non-repeating link cross references and that they have no repeats & that times are consistent
14395 
14396 /* Double crosslink (shuttle) table:
14397 
14398 Command Format OtherHead NonRepeating- LinkTrain- NonRepeating- Decsription
14399  Code ShuttleLink- EntryPtr ShuttleLink-
14400  HeadCode EntryPtr
14401 
14402 Snt-sh SNTShuttle Y (rtn shuttle) N Y (rtn sh) N Simple shuttle - no feeder service
14403 Frh-sh TimeCmdHeadCode Y (outwd shuttle) N Y (outwd sh) N Simple shuttle - no finishing service
14404 F-nshs FNSNonRepeatToShuttle N (shld be Y for outwd shuttle) Y (shld be N) Y (correct) N (correct) Feeder service link to shuttle
14405 Sns-sh SNSShuttle Y (rtn shuttle) Y (feeder) Y (rtn) Y (fdr) Shuttle link from feeder service
14406 Sns-fsh SNSNonRepeatFromShuttle N (shld be Y for rtn shuttle) Y (shld be N) Y (correct) N (correct) Finishing service link from shuttle
14407 Fns-sh FSHNewService Y (outwd shuttle) Y (finishing) Y (outwd sh) Y (finish) Shuttle link to finishing service
14408 
14409 Note: Any shuttle start can have any finish - feeder and finish, neither, feeder but no finish & vice versa.
14410 */
14411 
14412 {
14413  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckNonRepeatingShuttleLinksAndSetData," + MainHeadCode + "," +
14414  NonRepeatingHeadCode);
14415  int ForwardCount = 0;
14416  int ReverseCount = 0;
14417  unsigned int ForwardTDVectorNumber, ReverseTDVectorNumber;
14418  TActionVectorEntry *ReverseEntryPtr = 0, *ForwardEntryPtr = 0;
14419  // Forward corresponds to Main, Reverse to Other
14420  TTrainDataEntry *MainTrainDataPtr = 0;
14421  TTrainDataEntry *OtherTrainDataPtr = 0;
14422 
14423  // forward check
14424  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
14425  {
14426  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
14427  if(TDEntry.HeadCode == MainHeadCode)
14428  {
14429  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
14430  {
14431  TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
14432  if(AVEntry.NonRepeatingShuttleLinkHeadCode == NonRepeatingHeadCode)
14433  {
14434  MainTrainDataPtr = &TrainDataVector.at(x);
14435  ForwardEntryPtr = &AVEntry;
14436  ForwardCount++;
14437  ForwardTDVectorNumber = x;
14438  }
14439  }
14440  }
14441  }
14442  if(ForwardCount == 0)
14443  // this is an exception because the headcodes are selected in the same order as the forward check
14444  {
14445  throw Exception("Error, ForwardCount == 0 in CheckNonRepeatingShuttleLinksAndSetData after called with found values");
14446  }
14447  if(ForwardCount > 1)
14448  {
14449  SecondPassMessage(GiveMessages, "Error in timetable - found more than one reference to " + NonRepeatingHeadCode + " from a train whose headcode is " +
14450  MainHeadCode);
14451  TrainDataVector.clear();
14452  Utilities->CallLogPop(1061);
14453  return(false);
14454  }
14455  // reverse check
14456  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
14457  {
14458  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
14459  if(TDEntry.HeadCode == NonRepeatingHeadCode)
14460  {
14461  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
14462  {
14463  TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
14464  if(AVEntry.NonRepeatingShuttleLinkHeadCode == MainHeadCode)
14465  {
14466  OtherTrainDataPtr = &TrainDataVector.at(x);
14467  ReverseCount++;
14468  ReverseEntryPtr = &AVEntry;
14469  ReverseTDVectorNumber = x;
14470  }
14471  }
14472  }
14473  }
14474 
14475  if(ReverseCount == 0)
14476  {
14477  SecondPassMessage(GiveMessages, "Error in timetable - cross reference missing in either " + MainHeadCode + " or " + NonRepeatingHeadCode);
14478  TrainDataVector.clear();
14479  Utilities->CallLogPop(1062);
14480  return(false);
14481  }
14482  if(ReverseCount > 1)
14483  {
14484  SecondPassMessage(GiveMessages, "Error in timetable - found more than one reference to " + MainHeadCode + " from a train whose headcode is " +
14485  NonRepeatingHeadCode);
14486  TrainDataVector.clear();
14487  Utilities->CallLogPop(1063);
14488  return(false);
14489  }
14490  if(((ForwardEntryPtr->Command == "F-nshs") || (ForwardEntryPtr->Command == "Sns-fsh")) && (MainTrainDataPtr->ActionVector.back().FormatType == Repeat))
14491  {
14492  SecondPassMessage(GiveMessages, "Error in timetable - shuttle connecting train " + MainHeadCode + " shouldn't have a repeat");
14493  TrainDataVector.clear();
14494  Utilities->CallLogPop(1064);
14495  return(false);
14496  }
14497  if((ForwardEntryPtr->Command != "F-nshs") && (ForwardEntryPtr->Command != "Sns-fsh") && (MainTrainDataPtr->ActionVector.back().FormatType != Repeat))
14498  {
14499  SecondPassMessage(GiveMessages, "Error in timetable - shuttle train " + MainHeadCode + " does not have a repeat item");
14500  TrainDataVector.clear();
14501  Utilities->CallLogPop(1065);
14502  return(false);
14503  }
14504  if(ForwardEntryPtr->LocationName == "")
14505  {
14506  SecondPassMessage(GiveMessages, "Error in timetable - location error in cross referenced trains " + MainHeadCode + " and " + NonRepeatingHeadCode +
14507  ". One or other service does not have a location set");
14508  TrainDataVector.clear();
14509  Utilities->CallLogPop(1066);
14510  return(false);
14511  }
14512  if(ReverseEntryPtr->LocationName == "")
14513  {
14514  SecondPassMessage(GiveMessages, "Error in timetable - location error in cross referenced trains " + MainHeadCode + " and " + NonRepeatingHeadCode +
14515  ". One or other service does not have a location set");
14516  TrainDataVector.clear();
14517  Utilities->CallLogPop(1067);
14518  return(false);
14519  }
14520  if(ForwardEntryPtr->LocationName != ReverseEntryPtr->LocationName)
14521  {
14522  SecondPassMessage(GiveMessages, "Error in timetable - cross referenced train " + NonRepeatingHeadCode +
14523  " is at a different location to the referencing train " + MainHeadCode);
14524  TrainDataVector.clear();
14525  Utilities->CallLogPop(1068);
14526  return(false);
14527  }
14528  if(ForwardEntryPtr->Command == "F-nshs")
14529  // i.e. the non repeating link into the shuttle service
14530  {
14531  if(ForwardEntryPtr->EventTime != ReverseEntryPtr->EventTime)
14532  {
14533  SecondPassMessage(GiveMessages, "Error in timetable - shuttle in-link service " + MainHeadCode +
14534  " finish time not consistent with start time of shuttle service " + NonRepeatingHeadCode);
14535  TrainDataVector.clear();
14536  Utilities->CallLogPop(1069);
14537  return(false);
14538  }
14539  }
14540  if(ForwardEntryPtr->Command == "Fns-sh")
14541  // i.e. the non repeating link out from the shuttle service
14542  {
14543  if(!CheckNonRepeatingShuttleLinkTime(0, ForwardEntryPtr->EventTime, ReverseEntryPtr->EventTime,
14544  MainTrainDataPtr->ActionVector.back().RearStartOrRepeatMins, MainTrainDataPtr->ActionVector.back().NumberOfRepeats))
14545  {
14546  SecondPassMessage(GiveMessages, "Error in timetable - service " + NonRepeatingHeadCode + ", which links out from shuttle service " + MainHeadCode +
14547  ", has the wrong start time. It should correspond to the finish time of the last shuttle.");
14548  TrainDataVector.clear();
14549  Utilities->CallLogPop(1070);
14550  return(false);
14551  }
14552  }
14553  if((ForwardEntryPtr->Command == "F-nshs") || (ForwardEntryPtr->Command == "Sns-fsh"))
14554  // i.e. a non repeating link to or from the shuttle service
14555  {
14556  if(ReverseTDVectorNumber == ForwardTDVectorNumber)
14557  {
14558  SecondPassMessage(GiveMessages, "Error in timetable - the non repeating link service " + NonRepeatingHeadCode +
14559  " appears in the same sequence as the corresponding shuttle service " + MainHeadCode);
14560  TrainDataVector.clear();
14561  Utilities->CallLogPop(1071);
14562  return(false);
14563  }
14564  }
14565 /* it's allowed to have a different description
14566  if((ForwardEntryPtr->Command == "F-nshs") || (ForwardEntryPtr->Command == "Sns-fsh"))//i.e. a non repeating link to or from the shuttle service
14567  {
14568  if((MainTrainDataPtr->Description != "") && (OtherTrainDataPtr->Description != "") && (MainTrainDataPtr->Description != OtherTrainDataPtr->Description))
14569  {
14570  SecondPassMessage(GiveMessages, "Error in timetable - the non repeating link service " + NonRepeatingHeadCode + " has a different description to the corresponding shuttle service " + MainHeadCode);
14571  TrainDataVector.clear();
14572  Utilities->CallLogPop(1072);
14573  return false;
14574  }
14575  }
14576 */
14577  if(ForwardEntryPtr->Command == "Sns-sh")
14578  {
14579  if(ReverseEntryPtr->Command != "F-nshs")
14580  {
14581  SecondPassMessage(GiveMessages, "Error in timetable - unable to find a corresponding 'F-nshs' event for the 'Sns-sh' train whose headcode is " +
14582  MainHeadCode + " and is a new shuttle service formed from the service with headcode " + NonRepeatingHeadCode);
14583  TrainDataVector.clear();
14584  Utilities->CallLogPop(1073);
14585  return(false);
14586  }
14587  }
14588  if(ForwardEntryPtr->Command == "F-nshs")
14589  {
14590  if(ReverseEntryPtr->Command != "Sns-sh")
14591  {
14592  SecondPassMessage(GiveMessages, "Error in timetable - unable to find a corresponding 'Sns-sh' event for the 'F-nshs' train whose headcode is " +
14593  MainHeadCode + " and forms a new shuttle service with headcode " + NonRepeatingHeadCode);
14594  TrainDataVector.clear();
14595  Utilities->CallLogPop(1074);
14596  return(false);
14597  }
14598  else
14599  {
14600  ForwardEntryPtr->LinkedTrainEntryPtr = OtherTrainDataPtr;
14601  ReverseEntryPtr->NonRepeatingShuttleLinkEntryPtr = MainTrainDataPtr;
14602  if(OtherTrainDataPtr->Description == "")
14603  {
14604  OtherTrainDataPtr->Description = MainTrainDataPtr->Description;
14605  }
14606  // Probably redundant as this is continued from the earlier service when the changeover happens (also may not be set here yet if a service continuation)
14607  OtherTrainDataPtr->MaxRunningSpeed = MainTrainDataPtr->MaxRunningSpeed;
14608  // Probably redundant as this is continued from the earlier service when the changeover happens (also may not be set here yet if a service continuation)
14609  }
14610  }
14611  if(ForwardEntryPtr->Command == "Sns-fsh")
14612  {
14613  if(ReverseEntryPtr->Command != "Fns-sh")
14614  {
14615  SecondPassMessage(GiveMessages,
14616  "Error in timetable - unable to find a corresponding 'Fns-sh' event for the 'Sns-fsh' non-shuttle service whose headcode is " + MainHeadCode +
14617  " formed from a shuttle service with headcode " + NonRepeatingHeadCode);
14618  TrainDataVector.clear();
14619  Utilities->CallLogPop(1075);
14620  return(false);
14621  }
14622  }
14623  if(ForwardEntryPtr->Command == "Fns-sh")
14624  {
14625  if(ReverseEntryPtr->Command != "Sns-fsh")
14626  {
14627  SecondPassMessage(GiveMessages,
14628  "Error in timetable - unable to find a corresponding 'Sns-fsh' event for the 'Fns-sh' shuttle service whose headcode is " + MainHeadCode +
14629  " and forms a new non-shuttle service with headcode " + NonRepeatingHeadCode);
14630  TrainDataVector.clear();
14631  Utilities->CallLogPop(1076);
14632  return(false);
14633  }
14634  else
14635  {
14636  ForwardEntryPtr->NonRepeatingShuttleLinkEntryPtr = OtherTrainDataPtr;
14637  // links to the non-repeating non-shuttle linked service
14638  ReverseEntryPtr->LinkedTrainEntryPtr = MainTrainDataPtr;
14639  // needed for creating formatted timetable
14640  if(OtherTrainDataPtr->Description == "")
14641  {
14642  OtherTrainDataPtr->Description = MainTrainDataPtr->Description;
14643  }
14644  // Probably redundant as this is continued from the earlier service when the changeover happens (also may not be set here yet if a service continuation)
14645  OtherTrainDataPtr->MaxRunningSpeed = MainTrainDataPtr->MaxRunningSpeed;
14646  // Probably redundant as this is continued from the earlier service when the changeover happens (also may not be set here yet if a service continuation)
14647  }
14648  }
14649  Utilities->CallLogPop(1077);
14650  return(true);
14651 }
14652 
14653 // ---------------------------------------------------------------------------
14654 
14655 bool TTrainController::CheckNonRepeatingShuttleLinkTime(int Caller, TDateTime ForwardEventTime, TDateTime ReverseEventTime, int RepeatMinutes, int RepeatNumber)
14656 // Forward train is the finish shuttle entry 'Fns-sh'.
14657 // The Reverse (new non-repeating service) time must == Forward time + (RepeatMins * RepeatNumber) but allow 10 secs either side
14658 {
14659  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckNonRepeatingShuttleLinkTime," + AnsiString(double(ForwardEventTime))
14660  + "," + AnsiString(double(ReverseEventTime)) + "," + AnsiString(RepeatMinutes) + "," + AnsiString(RepeatNumber));
14661  int ForwardSecs = int(double(ForwardEventTime) * 86400);
14662  int ReverseSecs = int(double(ReverseEventTime) * 86400);
14663  int RepeatSecs = RepeatMinutes * RepeatNumber * 60;
14664 
14665  if((ReverseSecs > (ForwardSecs + RepeatSecs + 10)) || (ReverseSecs < (ForwardSecs + RepeatSecs - 10)))
14666  {
14667  Utilities->CallLogPop(1369);
14668  return(false);
14669  }
14670  else
14671  {
14672  Utilities->CallLogPop(1370);
14673  return(true);
14674  }
14675 }
14676 
14677 // ---------------------------------------------------------------------------
14678 
14679 bool TTrainController::CheckShuttleServiceIntegrity(int Caller, TTrainDataEntry *TDEntryPtr, bool GiveMessages)
14680 // check that each shuttle start ends either in Fns or Fxx-sh (though a single service can't end in Fxx-sh), and that
14681 // when the Fxx-sh is reached it references the original start and not another shuttle - not allowed to link two shuttles,
14682 // don't ever need to and as designed would skip repeats.
14683 
14684 // enter with TDEntry a shuttle start - Snt-sh or Sns-sh
14685 {
14686  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckShuttleServiceIntegrity," + AnsiString(TDEntryPtr->HeadCode));
14687  if(TDEntryPtr->ActionVector.back().FormatType != Repeat)
14688  {
14689  throw Exception("Error - last entry in " + TDEntryPtr->HeadCode + " service is not a repeat - should have already found this error");
14690  }
14691  TTrainDataEntry *ShuttleStartAddress = TDEntryPtr;
14692  AnsiString OriginalHeadCode = TDEntryPtr->HeadCode;
14693  AnsiString LastActionCommand = (TDEntryPtr->ActionVector.end() - 2)->Command;
14694 
14695  if((LastActionCommand != "Fns") && (LastActionCommand != "Fns-sh") && (LastActionCommand != "Frh-sh"))
14696  {
14697  SecondPassMessage(GiveMessages, "Error in timetable - last event in shuttle service " + TDEntryPtr->HeadCode + " is not 'Fns', 'Fns-sh' or 'Frh-sh'");
14698  TrainDataVector.clear();
14699  Utilities->CallLogPop(1091);
14700  return(false);
14701  }
14702  while(LastActionCommand == "Fns")
14703  {
14704  TDEntryPtr = (TDEntryPtr->ActionVector.end() - 2)->LinkedTrainEntryPtr;
14705  LastActionCommand = (TDEntryPtr->ActionVector.end() - 2)->Command;
14706  if((LastActionCommand != "Fns") && (LastActionCommand != "Fns-sh") && (LastActionCommand != "Frh-sh"))
14707  {
14708  SecondPassMessage(GiveMessages,
14709  "Error in timetable - last event in a continuation shuttle service (i.e links back to a shuttle) whose headcode is " + TDEntryPtr->HeadCode +
14710  " is not 'Fns', 'Fns-sh' or 'Frh-sh'");
14711  TrainDataVector.clear();
14712  Utilities->CallLogPop(1092);
14713  return(false);
14714  }
14715  }
14716  // exit the 'while' with LastActionCommand FSH-XX
14717  if((TDEntryPtr->ActionVector.end() - 2)->LinkedTrainEntryPtr != ShuttleStartAddress)
14718  {
14719  SecondPassMessage(GiveMessages, "Error in timetable - the event that ends service " + TDEntryPtr->HeadCode +
14720  " is a shuttle finish, but it doesn't link back to the start of the original shuttle starting service " + OriginalHeadCode +
14721  ". The linking of two or more shuttles is not permitted.");
14722  TrainDataVector.clear();
14723  Utilities->CallLogPop(1093);
14724  return(false);
14725  }
14726  Utilities->CallLogPop(1094);
14727  return(true);
14728 }
14729 
14730 // ---------------------------------------------------------------------------
14731 
14732 void TTrainController::TimetableMessage(bool GiveMessages, AnsiString Message)
14733 {
14734  if(!GiveMessages)
14735  {
14736  return;
14737  }
14738  // if(ServiceReference == "") ShowMessage(Message);
14739  if(!CheckHeadCodeValidity(12, false, ServiceReference))
14740  {
14741  ShowMessage(Message);
14742  }
14743  // changed from above at v2.3.0 as a meaningless value for 'Timetable invalid - unable to find a valid start time on its own line' (uses last entry text)
14744  // false means don't give messages within the function
14745  else
14746  {
14747  ShowMessage("Service " + ServiceReference + ": " + Message);
14748  }
14749 }
14750 
14751 // ---------------------------------------------------------------------------
14752 
14753 void TTrainController::SecondPassMessage(bool GiveMessages, AnsiString Message)
14754 {
14755  if(!GiveMessages)
14756  {
14757  return;
14758  }
14759  ShowMessage(Message);
14760 }
14761 
14762 // $$$$$$$$$$$$$$$$$$$$$$$ End of Timetable Functions $$$$$$$$$$$$$$$$$$$$$$$
14763 // ---------------------------------------------------------------------------
14764 
14765 void TTrainController::LogActionError(int Caller, AnsiString HeadCode, AnsiString OtherHeadCode, TActionEventType ActionEventType, AnsiString LocationID)
14766 // FailTrainEntry: 06:00:10 HELD: 2F43 can't enter railway, train obstructing entry position 57-N5
14767 // FailCreateTrain: 06:00:10 HELD: 2F43 can't be created, train obstructing start position 57-N5
14768 // FailCreateLockedRoute: 06:00:10 HELD: 2F43 can't be created on a locked route - start position 57-N5
14769 // FailEnterLockedRoute: 06:00:10 HELD: 2F43 can't enter on a locked route - start position 57-N5
14770 // FailCreatePoints: 06:00:10 HELD: 2F43 can't be created, points set to diverge at start position 57-N5
14771 // FailUnexpectedExitRailway: 06:00:10 ERROR: 2F43 left railway unexpectedly at position 57-N5
14772 // FailIncorrectExit: 06:00:10 ERROR: 2F43 left railway at an incorrect exit at position 57-N5
14773 // FailSPAD: 06:00:10 ERROR: 2F43 PASSED SIGNAL AT DANGER at position 57-N5
14774 // FailLockedRoute: 06:00:10 ERROR: SPAD Risk! Signals reset ahead of train, at position 57-N5
14775 // FailLocTooShort: 06:00:10 ERROR: 2F43 failed to split - location too short at Essex Road
14776 // FailSplitDueToOtherTrain: 06:00:10 HELD: 2F43 unable to split - another train is obstructing at Essex Road
14777 // FailCrashed: 06:00:10: ERROR: 2F43 CRASHED INTO 3F43 at position 46-N7
14778 // FailDerailed: 06:00:10: ERROR: 2F43 DERAILED at position 46-N7
14779 // FailUnexpectedBuffers: 06:00:10: ERROR: 2F43 stopped at buffers unexpectedly at position 46-N7
14780 // FailMissedArrival: 06:00:10: ERROR: 2F43 failed to stop at Essex Road;
14781 // FailMissedSplit: 06:00:10: ERROR: 2F43 failed to split at Essex Road
14782 // FailMissedJBO: 06:00:10: ERROR: 2F43 failed to be joined by join other train at Essex Road
14783 // FailMissedJoinOther: 06:00:10: ERROR: 2F43 failed to join other train at Essex Road
14784 // FailMissedTerminate: 06:00:10: ERROR: 2F43 failed to terminate at Essex Road
14785 // FailMissedNewService: 06:00:10: ERROR: 2F43 failed to form new service at Essex Road
14786 // FailMissedExitRailway: 06:00:10: ERROR: 2F43 failed to exit railway
14787 // FailMissedChangeDirection: 06:00:10: ERROR: 2F43 failed to change direction at Essex Road
14788 // FailMissedPass: 06:00:10: ERROR: 2F43 failed to pass Essex Road
14789 // FailBuffersPreventingStart: 06:00:10: ERROR: 2F43 facing buffers and unable to start at Essex Road
14790 // FailBufferCrash: 06:00:10: ERROR: 2F43 CRASHED INTO BUFFERS at 46-N7
14791 // FailLevelCrossingCrash: 06:00:10: ERROR: 2F43 CRASHED INTO ROAD TRAFFIC AT A LEVEL CROSSING at 46-N7
14792 // RouteForceCancelled: 06:00:10: ERROR: 2F43 forced a route cancellation by occupying it incorrectly at 46-N7
14793 // WaitingForJBO: 06:00:10: WARNING: 2F43 waiting to join 3F43 at Essex Road
14794 // WaitingForFJO: 06:00:10: WARNING: 2F43 waiting to be joined by 3F43 at Essex Road
14795 // FailEntryRouteSetAgainst: 06:00:10: WARNING: 2F43 can't enter railway, route set against it at entry position 57-N5
14796 {
14797  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",LogActionError," + HeadCode + "," + OtherHeadCode + "," +
14798  AnsiString(ActionEventType) + "," + LocationID);
14799  AnsiString BaseLog = "", Prefix = "", ErrorLog = "", WarningStr = "";
14800 
14801  TDateTime ActualTime = TrainController->TTClockTime; //moved from lower down at v2.9.1
14802  AnsiString TimeAndHeadCode = Utilities->Format96HHMMSS(ActualTime) + " " + HeadCode; //added at v2.9.1 to give more info to user
14803 
14804  Prefix = " ERROR: ";
14805  if(ActionEventType == FailTrainEntry)
14806  {
14807  Prefix = " HELD: ";
14808  ErrorLog = " can't enter railway, train obstructing entry position ";
14809  WarningStr = " can't enter railway, train obstructing entry position ";
14810  Display->WarningLog(1, TimeAndHeadCode + WarningStr + LocationID);
14811  }
14812  else if(ActionEventType == FailEntryRouteSetAgainst) //added at v2.9.1
14813  {
14814  Prefix = " HELD: ";
14815  ErrorLog = " can't enter railway, route set against it at entry position ";
14816  WarningStr = " can't enter railway, route set against it at entry position ";
14817  Display->WarningLog(10, TimeAndHeadCode + WarningStr + LocationID);
14818  }
14819  else if(ActionEventType == FailCreateTrain)
14820  {
14821  Prefix = " HELD: ";
14822  ErrorLog = " can't be created, train obstructing ";
14823  WarningStr = " can't be created, train obstructing ";
14824  Display->WarningLog(2, TimeAndHeadCode + WarningStr + LocationID);
14825  }
14826  else if(ActionEventType == FailCreateLockedRoute)
14827  {
14828  Prefix = " HELD: ";
14829  ErrorLog = " can't be created on a locked route at ";
14830  WarningStr = " can't be created on a locked route at ";
14831  Display->WarningLog(4, TimeAndHeadCode + WarningStr + LocationID);
14832  }
14833  else if(ActionEventType == FailEnterLockedRoute)
14834  {
14835  Prefix = " HELD: ";
14836  ErrorLog = " can't enter on a locked route at ";
14837  WarningStr = " can't enter on a locked route at ";
14838  Display->WarningLog(5, TimeAndHeadCode + WarningStr + LocationID);
14839  }
14840  else if(ActionEventType == FailCreatePoints)
14841  {
14842  Prefix = " HELD: ";
14843  ErrorLog = " can't be created, diverging points at ";
14844  WarningStr = " can't be created, diverging points at ";
14845  Display->WarningLog(3, TimeAndHeadCode + WarningStr + LocationID);
14846  }
14847  else if(ActionEventType == FailUnexpectedExitRailway)
14848  {
14849  ErrorLog = " left railway unexpectedly at ";
14850  UnexpectedExits++;
14851  }
14852  else if(ActionEventType == FailIncorrectExit)
14853  {
14854  ErrorLog = " left railway at an incorrect exit at ";
14855  IncorrectExits++;
14856  }
14857  else if(ActionEventType == FailLocTooShort)
14858  {
14859  ErrorLog = " failed to split - location too short at ";
14860  WarningStr = " failed to split, location too short at ";
14861  Display->WarningLog(6, TimeAndHeadCode + WarningStr + LocationID);
14862  }
14863  else if(ActionEventType == FailSplitDueToOtherTrain)
14864  {
14865  Prefix = " HELD: ";
14866  ErrorLog = " unable to split - other train obstructing at ";
14867  WarningStr = " unable to split - other train obstructing at ";
14868  Display->WarningLog(7, TimeAndHeadCode + WarningStr + LocationID);
14869  }
14870  else if(ActionEventType == FailUnexpectedBuffers)
14871  {
14872  ErrorLog = " stopped at buffers unexpectedly at position ";
14873  }
14874  else if(ActionEventType == FailMissedArrival)
14875  {
14876  ErrorLog = " failed to stop at ";
14877  MissedStops++;
14878  }
14879  else if(ActionEventType == FailMissedSplit)
14880  {
14881  ErrorLog = " failed to split at ";
14883  }
14884  else if(ActionEventType == FailMissedJBO)
14885  {
14886  ErrorLog = " failed to be joined by other train at ";
14888  }
14889  else if(ActionEventType == FailMissedJoinOther)
14890  {
14891  ErrorLog = " failed to join other train at ";
14893  }
14894  else if(ActionEventType == FailMissedTerminate)
14895  {
14896  ErrorLog = " failed to terminate at ";
14898  }
14899  else if(ActionEventType == FailMissedNewService)
14900  {
14901  ErrorLog = " failed to form new service at ";
14903  }
14904  else if(ActionEventType == FailMissedExitRailway)
14905  {
14906  ErrorLog = " failed to exit railway ";
14908  }
14909  else if(ActionEventType == FailMissedChangeDirection)
14910  {
14911  ErrorLog = " failed to change direction at ";
14913  }
14914  else if(ActionEventType == FailMissedPass)
14915  {
14916  ErrorLog = " failed to pass ";
14918  }
14919  else if(ActionEventType == FailBuffersPreventingStart)
14920  {
14921  ErrorLog = " facing buffers and unable to start at ";
14922  }
14923  else if(ActionEventType == FailDerailed)
14924  {
14925  ErrorLog = " DERAILED at position ";
14926  Prefix = " DERAILMENT: ";
14927  Derailments++;
14928  }
14929  else if(ActionEventType == FailBufferCrash)
14930  {
14931  ErrorLog = " CRASHED INTO BUFFERS at ";
14932  Prefix = " CRASH: ";
14933  CrashedTrains++;
14934  }
14935  else if(ActionEventType == FailLevelCrossingCrash)
14936  {
14937  ErrorLog = " CRASHED INTO ROAD TRAFFIC AT A LEVEL CROSSING at ";
14938  Prefix = " CRASH: ";
14939  CrashedTrains++;
14940  }
14941  else if(ActionEventType == FailCrashed)
14942  {
14943  ErrorLog = " CRASHED INTO " + OtherHeadCode + " at position ";
14944  Prefix = " CRASH: ";
14945  CrashedTrains++;
14946  CrashedTrains++;
14947  }
14948  else if(ActionEventType == FailSPAD)
14949  {
14950  ErrorLog = " PASSED SIGNAL AT DANGER at position ";
14951  Prefix = " SPAD: ";
14952  SPADEvents++;
14953  }
14954  else if(ActionEventType == FailLockedRoute)
14955  {
14956  ErrorLog = "Signals reset ahead of train, route cancelled at position ";
14957  Prefix = " SPAD RISK: ";
14958  SPADRisks++;
14959  }
14960  else if(ActionEventType == RouteForceCancelled)
14961  {
14962  ErrorLog = " forced a route cancellation by occupying it incorrectly at ";
14963  }
14964  else if(ActionEventType == WaitingForJBO)
14965  {
14966  Prefix = " WARNING: ";
14967  ErrorLog = " waiting to join " + OtherHeadCode + " at ";
14968  WarningStr = " waiting to join " + OtherHeadCode + " at ";
14969  Display->WarningLog(8, TimeAndHeadCode + WarningStr + LocationID);
14970  }
14971  else if(ActionEventType == WaitingForFJO)
14972  {
14973  Prefix = " WARNING: ";
14974  ErrorLog = " waiting to be joined by " + OtherHeadCode + " at ";
14975  WarningStr = " waiting to be joined by " + OtherHeadCode + " at ";
14976  Display->WarningLog(9, TimeAndHeadCode + WarningStr + LocationID);
14977  }
14978 
14979  BaseLog = Utilities->Format96HHMMSS(ActualTime) + Prefix + HeadCode;
14980  Display->PerformanceLog(4, BaseLog + ErrorLog + LocationID);
14981  Utilities->CallLogPop(1371);
14982 }
14983 
14984 // ---------------------------------------------------------------------------
14985 
14987 {
14988 /*
14989  TrainDataEntry
14990  AnsiString HeadCode, Description;//null on creation
14991  int StartSpeed, MaxRunningSpeed;//both kph
14992  int RepeatNumber;
14993  TActionVector ActionVector;
14994  TTrainOperatingDataVector TrainOperatingDataVector;//no of repeats + 1
14995  TTrainDataEntry() {StartSpeed=0; MaxRunningSpeed=0; RepeatNumber=0;}
14996 
14997  ActionVectorEntry
14998  TTimetableEntryType FormatType;
14999  TDateTime EventTime, ArrivalTime, DepartureTime;//zeroed on creation so change to -1 as a marker for 'not set'
15000  AnsiString LocationName, Command, OtherHeadCode;//null on creation
15001  TActionVectorEntry *OtherHeadCodeStartingEntryPtr;
15002  int RearStartOrRepeatMins, FrontStartOrRepeatDigits;
15003  int RepeatNumber;
15004 
15005  TrainOperatingData
15006  int Mass, MaxBrakeRate, PowerAtRail;//kg;m/s/s;W
15007  int TrainID;
15008  TRunningEntry RunningEntry;
15009  TDateTime StartTime;
15010  AnsiString HeadCode;
15011 */
15012  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SaveTrainDataVectorToFile");
15013  std::ofstream OutFile("TrainData.csv");
15014 
15015  if(OutFile == 0)
15016  {
15017  ShowMessage("Output file TrainData.csv failed to open");
15018  Utilities->CallLogPop(1372);
15019  return;
15020  }
15021  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
15022  {
15023  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
15024  OutFile << "HeadCode" << ',' << "Description" << ',' << "StartSpeed" << ',' << "MaxRunningSpeed" << ',' << "NumberOfTrains" << '\n' << '\n';
15025 
15026  OutFile << TDEntry.HeadCode.c_str() << ',' << TDEntry.Description.c_str()
15027  << ',' << TDEntry.StartSpeed << ',' << TDEntry.MaxRunningSpeed << ',' << TDEntry.NumberOfTrains << '\n' << '\n';
15028 
15029  OutFile << ',' << "FormatType" << ',' << "EventTime" << ',' << "ArrivalTime" << ',' << "DepartureTime" << ',' << "LocationName" << ',' << "Command" <<
15030  ',' << "OtherHeadCode" << ',' << "LinkedTrainEntryPtr" << ',' << "RearStartOrRepeatMins" << ',' << "FrontStartOrRepeatDigits" << ',' <<
15031  "RepeatNumber" << '\n' << '\n';
15032  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
15033  {
15034  TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
15035  AnsiString TimetableEntryTypeStr;
15036  // NoFormat, TimeLoc, TimeTimeLoc, TimeCmd, StartNew, TimeCmdHeadCode, FinRemHere, FNSNonRepeatToShuttle, SNTShuttle, SNSShuttle, SNSNonRepeatFromShuttle, FSHNewService, Repeat
15037  switch(AVEntry.FormatType)
15038  {
15039  case 0:
15040  {
15041  TimetableEntryTypeStr = "NoFormat";
15042  break;
15043  }
15044 
15045  case 1:
15046  {
15047  TimetableEntryTypeStr = "TimeLoc";
15048  break;
15049  }
15050 
15051  case 2:
15052  {
15053  TimetableEntryTypeStr = "TimeTimeLoc";
15054  break;
15055  }
15056 
15057  case 3:
15058  {
15059  TimetableEntryTypeStr = "TimeCmd";
15060  break;
15061  }
15062 
15063  case 4:
15064  {
15065  TimetableEntryTypeStr = "StartNew";
15066  break;
15067  }
15068 
15069  case 5:
15070  {
15071  TimetableEntryTypeStr = "TimeCmdHeadCode";
15072  break;
15073  }
15074 
15075  case 6:
15076  {
15077  TimetableEntryTypeStr = "FinRemHere";
15078  break;
15079  }
15080 
15081  case 7:
15082  {
15083  TimetableEntryTypeStr = "FNSShuttle";
15084  break;
15085  }
15086 
15087  case 8:
15088  {
15089  TimetableEntryTypeStr = "SNTShuttle";
15090  break;
15091  }
15092 
15093  case 9:
15094  {
15095  TimetableEntryTypeStr = "SNSShuttle";
15096  break;
15097  }
15098 
15099  case 10:
15100  {
15101  TimetableEntryTypeStr = "SNSNonRepeatFromShuttle";
15102  break;
15103  }
15104 
15105  case 11:
15106  {
15107  TimetableEntryTypeStr = "FSHNewService";
15108  break;
15109  }
15110 
15111  case 12:
15112  {
15113  TimetableEntryTypeStr = "Repeat";
15114  break;
15115  }
15116 
15117  default:
15118  {
15119  TimetableEntryTypeStr = "Default";
15120  break;
15121  }
15122  }
15123  OutFile << ',' << TimetableEntryTypeStr.c_str() << ',' << Utilities->Format96HHMM(AVEntry.EventTime).c_str() << ',' << Utilities->Format96HHMM
15124  (AVEntry.ArrivalTime).c_str() << ',' << Utilities->Format96HHMM(AVEntry.DepartureTime).c_str() << ',' << AVEntry.LocationName.c_str()
15125  << ',' << AVEntry.Command.c_str() << ',' << AVEntry.OtherHeadCode.c_str()
15126  << ',' << AVEntry.LinkedTrainEntryPtr << ',' << AVEntry.RearStartOrRepeatMins << ',' << AVEntry.FrontStartOrRepeatDigits << ',' <<
15127  AVEntry.NumberOfRepeats << '\n';
15128  }
15129  OutFile << '\n';
15130  OutFile << ',' << ',' << "Mass" << ',' << "MaxBrakeRate" << ',' << "PowerAtRail" << ',' << "TrainID" << ',' << "RunningEntry" << '\n' << '\n';
15131  for(unsigned int y = 0; y < TrainDataVector.at(x).TrainOperatingDataVector.size(); y++)
15132  {
15133  TTrainOperatingData TOD = TrainDataVector.at(x).TrainOperatingDataVector.at(y);
15134  AnsiString RunningEntryStr;
15135  // NotStarted, Running, Exited
15136  switch(TOD.RunningEntry)
15137  {
15138  case 0:
15139  {
15140  RunningEntryStr = "NotStarted";
15141  break;
15142  }
15143 
15144  case 1:
15145  {
15146  RunningEntryStr = "Running";
15147  break;
15148  }
15149 
15150  case 2:
15151  {
15152  RunningEntryStr = "Exited";
15153  break;
15154  }
15155  }
15156  OutFile << ',' << ',' << TOD.TrainID << ',' << RunningEntryStr.c_str() << ',' << '\n';
15157  }
15158  OutFile << '\n';
15159  }
15160  OutFile.close();
15161  Utilities->CallLogPop(1373);
15162 }
15163 
15164 // ---------------------------------------------------------------------------
15165 
15166 void TTrainController::StopTTClockMessage(int Caller, AnsiString Message)
15167 // ShowMessage stops everything so this function used where a message is needed when may be in Operating mode.
15168 // The timetable Restart and BaseTimes are reset so the timetable clock stops & restarts when 'OK' button pressed
15169 {
15170  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",StopTTClockMessage," + Message);
15171  StopTTClockFlag = true; // so TTClock stopped during MasterClockTimer function
15173  ShowMessage(Message);
15174  BaseTime = TDateTime::CurrentDateTime();
15175  StopTTClockFlag = false;
15176  Utilities->CallLogPop(1374);
15177 }
15178 
15179 // ---------------------------------------------------------------------------
15180 
15181 void TTrainController::SaveSessionTrains(int Caller, std::ofstream &SessionFile)
15182 // save *TrainDataEntryPtr & *ActionVectorEntryPtr as integer offsets
15183 // from the start of the relevant vectors. Can't save the pointer values
15184 // as these will be different each time the vectors are created
15185 {
15186  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SaveSessionTrains");
15187  Utilities->SaveFileInt(SessionFile, TrainVector.size());
15188  for(unsigned int x = 0; x < TrainVector.size(); x++)
15189  {
15190  TrainVectorAt(55, x).SaveOneSessionTrain(0, SessionFile);
15191  }
15192  Utilities->CallLogPop(1375);
15193 }
15194 
15195 // ---------------------------------------------------------------------------
15196 
15197 void TTrainController::LoadSessionTrains(int Caller, std::ifstream &SessionFile)
15198 {
15199  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",LoadSessionTrains");
15200  int NumberOfTrains = Utilities->LoadFileInt(SessionFile);
15201  TTrain *NewTrain = new TTrain(1, 0, 0, "", 0, 1, 0, 0, 0, (TTrainMode)0, 0, 0, 0, 0, 0); // have to have >0 for mass, else have divide
15202  // by zero error in calculating AValue, use 1
15203  for(int x = 0; x < NumberOfTrains; x++)
15204  {
15205  *NewTrain = TTrain(2, 0, 0, "", 0, 1, 0, 0, 0, (TTrainMode)0, 0, 0, 0, 0, 0); // have to have >0 for mass, else have divide
15206  // by zero error in calculating AValue, use 1
15207  NewTrain->LoadOneSessionTrain(0, SessionFile);
15208  if((NewTrain->EntrySpeed < 1) && (NewTrain->PowerAtRail < 1))
15209  // added at v2.4.0. have to include as that value not stored in session file
15210  {
15211  NewTrain->StoppedWithoutPower = true;
15212  }
15213  TrainVector.push_back(*NewTrain);
15214  LastTrainLoaded = x;
15215  }
15216  delete NewTrain;
15217  Utilities->CallLogPop(1376);
15218 }
15219 
15220 // ---------------------------------------------------------------------------
15221 
15222 bool TTrainController::CheckSessionTrains(int Caller, std::ifstream &InFile)
15223 {
15224  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckSessionTrains");
15225  int NumberOfTrains;
15226 
15227  if(!Utilities->CheckAndReadFileInt(InFile, 0, 10000, NumberOfTrains))
15228  {
15229  Utilities->CallLogPop(1377);
15230  return(false);
15231  }
15232  for(int x = 0; x < NumberOfTrains; x++)
15233  {
15234  if(!(TTrain::CheckOneSessionTrain(InFile)))
15235  {
15236  Utilities->CallLogPop(1378);
15237  return(false);
15238  }
15239  }
15240  Utilities->CallLogPop(1379);
15241  return(true);
15242 }
15243 
15244 // ---------------------------------------------------------------------------
15245 
15246 void TTrainController::SaveSessionLockedRoutes(int Caller, std::ofstream &SessionFile)
15247 {
15248  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SaveSessionLockedRoutes");
15249  Utilities->SaveFileInt(SessionFile, AllRoutes->LockedRouteVector.size());
15250  for(unsigned int x = 0; x < AllRoutes->LockedRouteVector.size(); x++)
15251  {
15252  Utilities->SaveFileInt(SessionFile, AllRoutes->LockedRouteVector.at(x).RouteNumber);
15253  Utilities->SaveFileInt(SessionFile, AllRoutes->LockedRouteVector.at(x).TruncateTrackVectorPosition);
15254  Utilities->SaveFileInt(SessionFile, AllRoutes->LockedRouteVector.at(x).LastTrackVectorPosition);
15255  Utilities->SaveFileInt(SessionFile, AllRoutes->LockedRouteVector.at(x).LastXLinkPos);
15256  Utilities->SaveFileDouble(SessionFile, double(AllRoutes->LockedRouteVector.at(x).LockStartTime));
15257  }
15258  Utilities->CallLogPop(1380);
15259 }
15260 
15261 // ---------------------------------------------------------------------------
15262 
15263 void TTrainController::LoadSessionLockedRoutes(int Caller, std::ifstream &SessionFile)
15264 {
15265  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",LoadSessionLockedRoutes");
15266  TAllRoutes::TLockedRouteClass LockedRouteObject;
15267  int LockedRouteVectorSize = Utilities->LoadFileInt(SessionFile);
15268 
15269  for(int x = 0; x < LockedRouteVectorSize; x++)
15270  {
15271  LockedRouteObject.RouteNumber = Utilities->LoadFileInt(SessionFile);
15272  LockedRouteObject.TruncateTrackVectorPosition = Utilities->LoadFileInt(SessionFile);
15273  LockedRouteObject.LastTrackVectorPosition = Utilities->LoadFileInt(SessionFile);
15274  LockedRouteObject.LastXLinkPos = Utilities->LoadFileInt(SessionFile);
15275  double LockStartTimeDouble = Utilities->LoadFileDouble(SessionFile);
15276  LockedRouteObject.LockStartTime = TDateTime(LockStartTimeDouble);
15277  AllRoutes->LockedRouteVector.push_back(LockedRouteObject);
15278  }
15279  Utilities->CallLogPop(1381);
15280 }
15281 
15282 // ---------------------------------------------------------------------------
15283 
15284 bool TTrainController::CheckSessionLockedRoutes(int Caller, std::ifstream &SessionFile)
15285 {
15286  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckSessionLockedRoutes");
15287  int LockedRouteVectorSize;
15288 
15289  if(!Utilities->CheckAndReadFileInt(SessionFile, 0, 10000, LockedRouteVectorSize))
15290  {
15291  Utilities->CallLogPop(1382);
15292  return(false);
15293  }
15294  for(int x = 0; x < LockedRouteVectorSize; x++)
15295  {
15296  if(!Utilities->CheckFileInt(SessionFile, 0, 1000000))
15297  {
15298  Utilities->CallLogPop(1383);
15299  return(false);
15300  }
15301  if(!Utilities->CheckFileInt(SessionFile, 0, 1000000))
15302  {
15303  Utilities->CallLogPop(1384);
15304  return(false);
15305  }
15306  if(!Utilities->CheckFileInt(SessionFile, 0, 1000000))
15307  {
15308  Utilities->CallLogPop(1385);
15309  return(false);
15310  }
15311  if(!Utilities->CheckFileInt(SessionFile, 0, 3))
15312  {
15313  Utilities->CallLogPop(1386);
15314  return(false);
15315  }
15316  if(!Utilities->CheckFileDouble(SessionFile))
15317  {
15318  Utilities->CallLogPop(1387);
15319  return(false);
15320  }
15321  }
15322  Utilities->CallLogPop(1388);
15323  return(true);
15324 }
15325 
15326 // ---------------------------------------------------------------------------
15327 
15328 void TTrainController::SaveSessionContinuationAutoSigEntries(int Caller, std::ofstream &SessionFile)
15329 {
15330  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SaveSessionContinuationAutoSigEntries");
15331  Utilities->SaveFileInt(SessionFile, ContinuationAutoSigVector.size());
15332  for(unsigned int x = 0; x < ContinuationAutoSigVector.size(); x++)
15333  {
15334  Utilities->SaveFileInt(SessionFile, ContinuationAutoSigVector.at(x).RouteNumber);
15335  Utilities->SaveFileInt(SessionFile, ContinuationAutoSigVector.at(x).AccessNumber);
15336  Utilities->SaveFileDouble(SessionFile, ContinuationAutoSigVector.at(x).FirstDelay);
15337  Utilities->SaveFileDouble(SessionFile, ContinuationAutoSigVector.at(x).SecondDelay);
15338  Utilities->SaveFileDouble(SessionFile, ContinuationAutoSigVector.at(x).ThirdDelay);
15339  Utilities->SaveFileDouble(SessionFile, double(ContinuationAutoSigVector.at(x).PassoutTime));
15340  }
15341  Utilities->CallLogPop(1389);
15342 }
15343 
15344 // ---------------------------------------------------------------------------
15345 
15346 void TTrainController::LoadSessionContinuationAutoSigEntries(int Caller, std::ifstream &SessionFile)
15347 {
15348  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",LoadSessionContinuationAutoSigEntries");
15349  TContinuationAutoSigEntry ContinuationAutoSigObject;
15350  int ContinuationAutoSigVectorSize = Utilities->LoadFileInt(SessionFile);
15351 
15352  for(int x = 0; x < ContinuationAutoSigVectorSize; x++)
15353  {
15354  ContinuationAutoSigObject.RouteNumber = Utilities->LoadFileInt(SessionFile);
15355  ContinuationAutoSigObject.AccessNumber = Utilities->LoadFileInt(SessionFile);
15356  ContinuationAutoSigObject.FirstDelay = Utilities->LoadFileDouble(SessionFile);
15357  ContinuationAutoSigObject.SecondDelay = Utilities->LoadFileDouble(SessionFile);
15358  ContinuationAutoSigObject.ThirdDelay = Utilities->LoadFileDouble(SessionFile);
15359  double PassoutTimeDouble = Utilities->LoadFileDouble(SessionFile);
15360  ContinuationAutoSigObject.PassoutTime = TDateTime(PassoutTimeDouble);
15361  ContinuationAutoSigVector.push_back(ContinuationAutoSigObject);
15362  }
15363  Utilities->CallLogPop(1390);
15364 }
15365 
15366 // ---------------------------------------------------------------------------
15367 
15368 bool TTrainController::CheckSessionContinuationAutoSigEntries(int Caller, std::ifstream &SessionFile)
15369 {
15370  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckSessionContinuationAutoSigEntries");
15371  int ContinuationAutoSigVectorSize;
15372 
15373  if(!Utilities->CheckAndReadFileInt(SessionFile, 0, 10000, ContinuationAutoSigVectorSize))
15374  {
15375  Utilities->CallLogPop(1391);
15376  return(false);
15377  }
15378  for(int x = 0; x < ContinuationAutoSigVectorSize; x++)
15379  {
15380  if(!Utilities->CheckFileInt(SessionFile, 0, 1000000))
15381  {
15382  Utilities->CallLogPop(1392);
15383  return(false);
15384  }
15385  if(!Utilities->CheckFileInt(SessionFile, 0, 3))
15386  {
15387  Utilities->CallLogPop(1393);
15388  return(false);
15389  }
15390  if(!Utilities->CheckFileDouble(SessionFile))
15391  {
15392  Utilities->CallLogPop(1405);
15393  return(false);
15394  }
15395  if(!Utilities->CheckFileDouble(SessionFile))
15396  {
15397  Utilities->CallLogPop(1406);
15398  return(false);
15399  }
15400  if(!Utilities->CheckFileDouble(SessionFile))
15401  {
15402  Utilities->CallLogPop(1407);
15403  return(false);
15404  }
15405  if(!Utilities->CheckFileDouble(SessionFile))
15406  {
15407  Utilities->CallLogPop(1394);
15408  return(false);
15409  }
15410  }
15411  Utilities->CallLogPop(1395);
15412  return(true);
15413 }
15414 
15415 // ---------------------------------------------------------------------------
15416 
15417 /*
15418  class TContinuationTrainExpectationEntry //for expected trains at continuation entries
15419  {
15420  public:
15421  AnsiString Description; ///< service description
15422  AnsiString HeadCode; ///< service headcode
15423  int RepeatNumber; ///< service RepeatNumber
15424  int IncrementalMinutes; ///< Repeat separation in minutes
15425  int IncrementalDigits; ///< Repeat headcode separation
15426  int VectorPosition; ///< TrackVectorPosition for the continuation element
15427  TTrainDataEntry *TrainDataEntryPtr; ///< points to the service entry in the timetable's TrainDataVector
15428  };
15429 
15430 
15431  typedef std::multimap<TDateTime,TContinuationTrainExpectationEntry> TContinuationTrainExpectationMultiMap;
15432  typedef pair<TDateTime, TContinuationTrainExpectationEntry> TContinuationTrainExpectationMultiMapPair;
15433 */
15434 
15436 // build this into timetable load so session loading can use it too
15437 // being a multimap it automatically sorts in ascending EventTime order
15438 {
15439  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",BuildContinuationTrainExpectationMultiMap");
15441  // need to clear as this called twice when load a session
15442  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
15443  {
15444  TTrainDataEntry & TDEntry = TrainDataVector.at(x);
15445  const TActionVectorEntry &AVFirstEntry = TDEntry.ActionVector.at(0);
15446  const TActionVectorEntry &AVLastEntry = TDEntry.ActionVector.at(TDEntry.ActionVector.size() - 1);
15447 
15448  if(AVFirstEntry.Command == "Snt")
15449  // new train (no need to include Snt-sh since they can't start at a continuation)
15450  {
15453  {
15455  CTEEntry.VectorPosition = AVFirstEntry.RearStartOrRepeatMins;
15456  // retains this value for all repeats
15457  CTEEntry.RepeatNumber = 0; // for first entry
15458  CTEEntry.TrainDataEntryPtr = &TDEntry;
15459  // retains this value for all repeats
15460  CTEEntry.HeadCode = TDEntry.HeadCode;
15461  CTEEntry.Description = TDEntry.Description;
15462  CTEEntry.IncrementalMinutes = 0;
15463  CTEEntry.IncrementalDigits = 0;
15464  if(AVLastEntry.FormatType == Repeat)
15465  {
15466  CTEEntry.IncrementalMinutes = AVLastEntry.RearStartOrRepeatMins;
15467  // retains this value or 0 for all repeats
15468  CTEEntry.IncrementalDigits = AVLastEntry.FrontStartOrRepeatDigits;
15469  // retains this value or 0 for all repeats
15470  }
15471  CTEMMP.first = AVFirstEntry.EventTime;
15472  CTEMMP.second = CTEEntry;
15473  ContinuationTrainExpectationMultiMap.insert(CTEMMP);
15474  // base entry
15475  if(TDEntry.NumberOfTrains > 1)
15476  {
15477  if(AVLastEntry.FormatType != Repeat)
15478  {
15479  throw Exception("Error, Last ActionVectorEntry not a repeat in BuildContinuationTrainExpectationMultiMap");
15480  }
15481  for(int y = 1; y < TDEntry.NumberOfTrains; y++)
15482  {
15483  CTEEntry.RepeatNumber = y;
15484  CTEEntry.HeadCode = GetRepeatHeadCode(23, TDEntry.HeadCode, y, AVLastEntry.FrontStartOrRepeatDigits);
15485  // CTEEntry.VectorPosition stays same
15486  CTEMMP.first = GetRepeatTime(3, AVFirstEntry.EventTime, y, AVLastEntry.RearStartOrRepeatMins);
15487  CTEMMP.second = CTEEntry;
15488  ContinuationTrainExpectationMultiMap.insert(CTEMMP);
15489  }
15490  }
15491  }
15492  }
15493  }
15494  Utilities->CallLogPop(1396);
15495 }
15496 
15497 // ---------------------------------------------------------------------------
15498 
15500 {
15501  // called when WarningFlashCount == 0 or when press zoomout button
15502  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PlotTrainsInZoomOutMode");
15503  if(!Display->ZoomOutFlag)
15504  {
15505  Utilities->CallLogPop(1156);
15506  return;
15507  }
15508  for(unsigned int x = 0; x < TrainVector.size(); x++)
15509  {
15510  // plot blanks & track for all train, even if to be overplotted, since when flashing need to overplot all anyway
15511  // if OldPlotElement[x] == -1 then ignore (not plotted)
15513  TrainVectorAt(57, x).PlotTrainInZoomOutMode(0, Flash);
15514  }
15515  Display->Update();
15516  // need to keep this since Update() not called for PlotSmallOutput as too slow
15517  Utilities->CallLogPop(742);
15518 }
15519 
15520 // ---------------------------------------------------------------------------
15521 
15522 TTrain &TTrainController::TrainVectorAt(int Caller, int VecPos)
15523 {
15524  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TrainVectorAt," + AnsiString(VecPos));
15525  if((VecPos < 0) || (VecPos >= (int)TrainVector.size()))
15526  {
15527  throw Exception("Out of Range Error, vector size: " + AnsiString(TrainVector.size()) + ", VecPos: " + AnsiString(VecPos) + " in TrainVectorAt");
15528  }
15529  Utilities->CallLogPop(740);
15530  return(TrainVector.at(VecPos));
15531 }
15532 
15533 // ---------------------------------------------------------------------------
15534 
15535 void TTrainController::CreateFormattedTimetable(int Caller, AnsiString RailwayTitle, AnsiString TimetableTitle, AnsiString CurDir)
15536 {
15537  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CreateFormattedTimetable");
15538  AnsiString RetStr = "", PartStr = "";
15539 
15540 
15541 /*
15542  Have description & mass etc for train at top - header, then array of actions
15543 
15544  class TActionVectorEntry
15545  {
15546  public:
15547  AnsiString LocationName, Command, OtherHeadCode, NonRepeatingShuttleLinkHeadCode;
15549  bool SignallerControl;
15551  bool Warning;
15553  int NumberOfRepeats;
15555  int RearStartOrRepeatMins, FrontStartOrRepeatDigits;
15557  TDateTime EventTime, ArrivalTime, DepartureTime;
15559  TNumList ExitList;
15561  TTimetableFormatType FormatType;
15563  TTimetableLocationType LocationType;
15565  TTimetableSequenceType SequenceType;
15567  TTimetableShuttleLinkType ShuttleLinkType;
15569  TTrainDataEntry *LinkedTrainEntryPtr;
15571  TTrainDataEntry *NonRepeatingShuttleLinkEntryPtr;
15573 
15574  typedef std::vector<TActionVectorEntry> TActionVector;//contains all actions for a single train
15575 
15576  enum TRunningEntry {NotStarted, Running, Exited};//contains status info for each train
15577 
15578  class TTrainOperatingData
15579  {
15580  public:
15581  int TrainID;
15582  TActionEventType EventReported;
15583  TRunningEntry RunningEntry;
15584 
15585  //inline function
15586  TTrainOperatingData() {TrainID = -1; EventReported= NoEvent; RunningEntry=NotStarted;}//ID -1 = marker for not running
15587  };
15588 
15589  typedef std::vector<TTrainOperatingData> TTrainOperatingDataVector;
15590 
15591  class TTrainDataEntry
15592  {
15593  public:
15594  AnsiString HeadCode, ServiceReference, Description;
15596  double MaxBrakeRate;
15598  double MaxRunningSpeed;
15600  double PowerAtRail;
15602  int Mass;
15604  int NumberOfTrains;
15606  int SignallerSpeed;
15608  int StartSpeed;
15610  TActionVector ActionVector;
15612  TTrainOperatingDataVector TrainOperatingDataVector;
15614 
15615  //inline function
15616  TTrainDataEntry() {StartSpeed=0; MaxRunningSpeed=0; NumberOfTrains=0;}
15617  };
15618 
15619  typedef std::vector<TTrainDataEntry> TTrainDataVector;//object is a member of TTrainController & contains the whole timetable
15620 
15621  //formatted timetable types
15622  class TOneTrainFormattedEntry
15623  {
15624  AnsiString Action;//includes location if relevanr
15625  AnsiString Time;
15626  };
15627 
15628  typedef std::vector<TOneTrainFormattedEntry> TOneFormattedTrainVector;
15629 
15630  class TOneCompleteFormattedTrain//headcode + list of actions
15631  {
15632  public:
15633  AnsiString HeadCode;
15634  TOneFormattedTrainVector OneFormattedTrainVector;
15635  };
15636 
15637  typedef std::vector<TOneCompleteFormattedTrain> TOneCompleteFormattedTrainVector;//list af all repeats
15638 
15639  class TTrainFormattedInformation//contains all information for a single TT entry (including repeats)
15640  {
15641  public:
15642  AnsiString Header;//description, mass, power, brake rate etc
15643  int NumberOfTrains;// number of repeats + 1
15644  TOneCompleteFormattedTrainVector OneCompleteFormattedTrainVector;//list af all repeats
15645  };
15646 
15647 
15648  typedef std::vector<TTrainFormattedInformation> TAllFormattedTrains;//all timetable in formatted form
15649  //end of formatted timetable types
15650 
15651 */
15652 
15653  AnsiString TTFileName = TDateTime::CurrentDateTime().FormatString("dd-mm-yyyy hh.nn.ss");
15654 
15655  // format "16/06/2009 20:55:17"
15656  // avoid characters in filename:= / \ : * ? " < > |
15657  TTFileName = CurDir + "\\Formatted timetables\\Timetable " + TTFileName + "; " + RailwayTitle + "; " + TimetableTitle + ".csv";
15658 
15659  AnsiString ShortTTName = "";
15660 
15661  for(int x = TTFileName.Length(); x > 0; x--)
15662  {
15663  if(TTFileName[x] == '\\')
15664  {
15665  ShortTTName = TTFileName.SubString(x + 1, TTFileName.Length() - x - 4);
15666  break;
15667  }
15668  }
15669 
15670  ShowMessage("Creates two timetables named " + ShortTTName +
15671  " in the 'Formatted timetables' folder, one in service order in '.csv' format, and one in chronological order in '.txt' format");
15672 
15673  Screen->Cursor = TCursor(-11); // Hourglass
15674 
15675  AnsiString FormatNoDPStr = "#######0";
15676  AnsiString TableTitle = "", TimetableTimeStr = "", MassStr = "", PowerStr = "", BrakeStr = "", MaxSpeedStr = "", FirstHeadCode = "", Header = "";
15677 
15679  TableTitle = "Railway: " + RailwayTitle + "; Timetable: " + TimetableTitle + "; Start time: " + TimetableTimeStr;
15680  TAllFormattedTrains *AllTTTrains = new TAllFormattedTrains;
15681 
15682  // all timetable in formatted form
15683  //create the AllTTTrains vector
15684  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
15685  {
15686  MassStr = "", PowerStr = "", BrakeStr = "", MaxSpeedStr = "";
15687  const TTrainDataEntry &TrainDataEntry = TrainDataVector.at(x);
15688  if(TrainDataEntry.Mass > 0)
15689  {
15690  MassStr = "; Mass " + AnsiString::FormatFloat(FormatNoDPStr, ((double)TrainDataEntry.Mass) / 1000) + "Te; ";
15691  }
15692  if(TrainDataEntry.PowerAtRail > 0)
15693  {
15694  PowerStr = "Power " + AnsiString::FormatFloat(FormatNoDPStr, TrainDataEntry.PowerAtRail / 1000 / 0.8) + "kW; ";
15695  }
15696  if(TrainDataEntry.MaxBrakeRate > 0)
15697  {
15698  BrakeStr = "Brake force " + AnsiString::FormatFloat(FormatNoDPStr, (TrainDataEntry.MaxBrakeRate * TrainDataEntry.Mass / 9810)) + "Te; ";
15699  }
15700  if(TrainDataEntry.MaxRunningSpeed > 0)
15701  {
15702  MaxSpeedStr = "Maximum speed " + AnsiString::FormatFloat(FormatNoDPStr, TrainDataEntry.MaxRunningSpeed) + " km/h";
15703  }
15704  FirstHeadCode = TrainDataEntry.HeadCode;
15705  int IncDigits = 0, IncMinutes = 0;
15706  const TActionVector &ActionVector = TrainDataEntry.ActionVector;
15707  if(!ActionVector.empty())
15708  {
15709  if(ActionVector.at(ActionVector.size() - 1).FormatType == Repeat)
15710  {
15711  IncDigits = ActionVector.at(ActionVector.size() - 1).FrontStartOrRepeatDigits;
15712  IncMinutes = ActionVector.at(ActionVector.size() - 1).RearStartOrRepeatMins;
15713  }
15714  }
15715  TTrainFormattedInformation OneTTLine;
15716  // contains all information for a single TT entry (including repeats)
15717  for(int y = 0; y < TrainDataEntry.NumberOfTrains; y++)
15718  {
15719  OneTTLine.Header = "";
15720  if((TrainDataEntry.Description != "") && (MassStr != ""))
15721  {
15722  OneTTLine.Header = TrainDataEntry.Description + MassStr + PowerStr + BrakeStr + MaxSpeedStr;
15723  }
15724  else if(TrainDataEntry.Description != "")
15725  {
15726  OneTTLine.Header = TrainDataEntry.Description;
15727  }
15728  OneTTLine.NumberOfTrains = TrainDataEntry.NumberOfTrains;
15729  TOneCompleteFormattedTrain OneTTTrain; // headcode + list of actions
15730  for(unsigned int z = 0; z < ActionVector.size(); z++)
15731  {
15732  TOneTrainFormattedEntry OneTTEntry;
15733  OneTTTrain.HeadCode = GetRepeatHeadCode(24, FirstHeadCode, y, IncDigits);
15734  TActionVectorEntry ActionVectorEntry = ActionVector.at(z);
15735  AnsiString PartStr = "", TimeStr = "";
15736 /*
15737  enum TTimetableFormatType {NoFormat, TimeLoc, TimeTimeLoc, TimeCmd, StartNew, TimeCmdHeadCode, FinRemHere,
15738  FNSNonRepeatToShuttle, SNTShuttle, SNSShuttle, SNSNonRepeatFromShuttle, FSHNewService, Repeat, PassTime,
15739  ExitRailway};
15740  enum TTimetableSequenceType {NoSequence, Start, Finish, Intermediate, SequTypeForRepeatEntry};
15741  enum TTimetableLocationType {NoLocation, AtLocation, EnRoute, LocTypeForRepeatEntry};
15742  enum TTimetableShuttleLinkType {NoShuttleLink, NotAShuttleLink, ShuttleLink, ShuttleLinkTypeForRepeatEntry};
15743 */
15744  if(ActionVectorEntry.SequenceType == Start)
15745  {
15746  if(ActionVectorEntry.FormatType == StartNew)
15747  {
15748  if(ActionVectorEntry.LocationName != "")
15749  {
15750  if(Track->TrackElementAt(742, ActionVectorEntry.RearStartOrRepeatMins).TrackType == Continuation)
15751  {
15752  PartStr = "Enters at " + ActionVectorEntry.LocationName;
15753  }
15754  else
15755  {
15756  PartStr = "Created at " + ActionVectorEntry.LocationName;
15757  }
15758  }
15759  else // may be a named continuation or other element, and if so report that
15760  {
15761  AnsiString LocName = Track->TrackElementAt(739, ActionVectorEntry.RearStartOrRepeatMins).ActiveTrackElementName;
15762  if(Track->TrackElementAt(740, ActionVectorEntry.RearStartOrRepeatMins).TrackType == Continuation)
15763  {
15764  if(LocName != "")
15765  {
15766  PartStr = "Enters at " + LocName;
15767  }
15768  else // use rear position if it's a continuation
15769  {
15770  PartStr = "Enters at " + Track->TrackElementAt(737, ActionVectorEntry.RearStartOrRepeatMins).ElementID;
15771  }
15772  }
15773  else // not a continuation
15774  {
15775  if(LocName != "")
15776  // if not a continuation then LocName should be same as ActionVectorEntry.LocationName
15777  // but include anyway
15778  {
15779  PartStr = "Created at " + LocName;
15780  }
15781  else // use rear position again
15782  {
15783  PartStr = "Created at " + Track->TrackElementAt(741, ActionVectorEntry.RearStartOrRepeatMins).ElementID;
15784  }
15785  }
15786  }
15787  TimeStr = Utilities->Format96HHMM(GetRepeatTime(20, ActionVectorEntry.EventTime, y, IncMinutes));
15788  }
15789  else if(ActionVectorEntry.FormatType == SNTShuttle)
15790  {
15791  if(y == 0) // first train
15792  {
15793  PartStr = "Enters at " + ActionVectorEntry.LocationName;
15794  TimeStr = Utilities->Format96HHMM(GetRepeatTime(21, ActionVectorEntry.EventTime, y, IncMinutes));
15795  }
15796  else
15797  {
15798  PartStr = "Repeat shuttle service at " + ActionVectorEntry.LocationName + " from ";
15799  TimeStr = GetRepeatHeadCode(45, ActionVectorEntry.OtherHeadCode, y - 1, IncDigits) + " at " +
15800  Utilities->Format96HHMM(GetRepeatTime(26, ActionVectorEntry.EventTime, y, IncMinutes));
15801  } // y-1 for headcode above since it is the last repeat value that the train is from
15802 
15803  }
15804  else if(ActionVectorEntry.Command == "Sfs")
15805  {
15806  PartStr = "New service at " + ActionVectorEntry.LocationName + " split from";
15807  TimeStr = GetRepeatHeadCode(33, ActionVectorEntry.OtherHeadCode, y, IncDigits) + " at " +
15808  Utilities->Format96HHMM(GetRepeatTime(24, ActionVectorEntry.EventTime, y, IncMinutes));
15809  }
15810  else if(ActionVectorEntry.Command == "Sns")
15811  {
15812  PartStr = "New service at " + ActionVectorEntry.LocationName + " from";
15813  TimeStr = GetRepeatHeadCode(34, ActionVectorEntry.OtherHeadCode, y, IncDigits) + " at " +
15814  Utilities->Format96HHMM(GetRepeatTime(25, ActionVectorEntry.EventTime, y, IncMinutes));
15815  }
15816  else if(ActionVectorEntry.FormatType == SNSShuttle)
15817  {
15818  if(y == 0) // first entry from shuttle
15819  {
15820  PartStr = "New service at " + ActionVectorEntry.LocationName + " from";
15821  TimeStr = ActionVectorEntry.NonRepeatingShuttleLinkHeadCode + " at " +
15822  Utilities->Format96HHMM(GetRepeatTime(27, ActionVectorEntry.EventTime, y, IncMinutes));
15823  }
15824  else
15825  {
15826  PartStr = "Repeat shuttle service at " + ActionVectorEntry.LocationName + " from ";
15827  TimeStr = GetRepeatHeadCode(35, ActionVectorEntry.OtherHeadCode, y - 1, IncDigits) + " at " +
15828  Utilities->Format96HHMM(GetRepeatTime(22, ActionVectorEntry.EventTime, y, IncMinutes));
15829  } // y-1 for headcode above since it is the last repeat value that the train is from
15830 
15831  }
15832  else if(ActionVectorEntry.FormatType == SNSNonRepeatFromShuttle)
15833  {
15834  PartStr = "New service at " + ActionVectorEntry.LocationName + " from";
15835  // need repeat for the non-repeating headcode as it's the last train of the repeating shuttle
15836  TTrainDataEntry *TDE = ActionVectorEntry.LinkedTrainEntryPtr;
15837  AnsiString FirstHeadCode = TDE->HeadCode;
15838  int LastRepeatNumber = TDE->NumberOfTrains - 1;
15839  // a shuttle has to have at least 1 repeat
15840  int IncrementalDigits = TDE->ActionVector.at(TDE->ActionVector.size() - 1).FrontStartOrRepeatDigits;
15841  TimeStr = GetRepeatHeadCode(36, FirstHeadCode, LastRepeatNumber, IncrementalDigits) + " at " +
15842  Utilities->Format96HHMM(GetRepeatTime(23, ActionVectorEntry.EventTime, y, IncMinutes));
15843  }
15844  }
15845  else if(ActionVectorEntry.SequenceType == Intermediate)
15846  {
15847  if(ActionVectorEntry.FormatType == TimeTimeLoc)
15848  {
15849  // here need 2 entries if times different so push the first right away & the second later
15850  // if times same just give the arrival entry
15851  if(ActionVectorEntry.DepartureTime != ActionVectorEntry.ArrivalTime)
15852  {
15853  PartStr = "Arrives at " + ActionVectorEntry.LocationName;
15854  TimeStr = Utilities->Format96HHMM(GetRepeatTime(4, ActionVectorEntry.ArrivalTime, y, IncMinutes));
15855  OneTTEntry.Action = PartStr;
15856  OneTTEntry.Time = TimeStr;
15857  OneTTTrain.OneFormattedTrainVector.push_back(OneTTEntry);
15858  PartStr = "Departs from " + ActionVectorEntry.LocationName;
15859  TimeStr = Utilities->Format96HHMM(GetRepeatTime(5, ActionVectorEntry.DepartureTime, y, IncMinutes));
15860  }
15861  else
15862  {
15863  PartStr = "Arrives & departs " + ActionVectorEntry.LocationName;
15864  TimeStr = Utilities->Format96HHMM(GetRepeatTime(29, ActionVectorEntry.ArrivalTime, y, IncMinutes));
15865  }
15866  }
15867  else if((ActionVectorEntry.FormatType == TimeLoc) && (ActionVectorEntry.ArrivalTime != TDateTime(-1)))
15868  {
15869  PartStr = "Arrives at " + ActionVectorEntry.LocationName;
15870  TimeStr = Utilities->Format96HHMM(GetRepeatTime(6, ActionVectorEntry.ArrivalTime, y, IncMinutes));
15871  }
15872  else if((ActionVectorEntry.FormatType == TimeLoc) && (ActionVectorEntry.ArrivalTime == TDateTime(-1)))
15873  {
15874  PartStr = "Departs from " + ActionVectorEntry.LocationName;
15875  TimeStr = Utilities->Format96HHMM(GetRepeatTime(7, ActionVectorEntry.DepartureTime, y, IncMinutes));
15876  }
15877  else if(ActionVectorEntry.FormatType == PassTime)
15878  {
15879  PartStr = "Passes " + ActionVectorEntry.LocationName;
15880  TimeStr = Utilities->Format96HHMM(GetRepeatTime(8, ActionVectorEntry.EventTime, y, IncMinutes));
15881  }
15882  else if(ActionVectorEntry.Command == "jbo")
15883  {
15884  PartStr = "Joined at " + ActionVectorEntry.LocationName + " by";
15885  TimeStr = GetRepeatHeadCode(37, ActionVectorEntry.OtherHeadCode, y, IncDigits) + " at " +
15886  Utilities->Format96HHMM(GetRepeatTime(9, ActionVectorEntry.EventTime, y, IncMinutes));
15887  }
15888  else if(ActionVectorEntry.Command == "fsp")
15889  {
15890  PartStr = "Splits from front at " + ActionVectorEntry.LocationName + " to form";
15891  TimeStr = GetRepeatHeadCode(38, ActionVectorEntry.OtherHeadCode, y, IncDigits) + " at " +
15892  Utilities->Format96HHMM(GetRepeatTime(10, ActionVectorEntry.EventTime, y, IncMinutes));
15893  }
15894  else if(ActionVectorEntry.Command == "rsp")
15895  {
15896  PartStr = "Splits from rear at " + ActionVectorEntry.LocationName + " to form";
15897  TimeStr = GetRepeatHeadCode(39, ActionVectorEntry.OtherHeadCode, y, IncDigits) + " at " +
15898  Utilities->Format96HHMM(GetRepeatTime(11, ActionVectorEntry.EventTime, y, IncMinutes));
15899  }
15900  else if(ActionVectorEntry.Command == "cdt")
15901  {
15902  PartStr = "Changes direction at " + ActionVectorEntry.LocationName;
15903  TimeStr = Utilities->Format96HHMM(GetRepeatTime(12, ActionVectorEntry.EventTime, y, IncMinutes));
15904  }
15905  }
15906  else if(ActionVectorEntry.SequenceType == Finish)
15907  {
15908  if(ActionVectorEntry.Command == "Fns")
15909  {
15910  PartStr = "At " + ActionVectorEntry.LocationName + " forms new service";
15911  TimeStr = GetRepeatHeadCode(40, ActionVectorEntry.OtherHeadCode, y, IncDigits) + " at " +
15912  Utilities->Format96HHMM(GetRepeatTime(13, ActionVectorEntry.EventTime, y, IncMinutes));
15913  }
15914  else if(ActionVectorEntry.Command == "F-nshs")
15915  {
15916  PartStr = "At " + ActionVectorEntry.LocationName + " forms new service";
15917  TimeStr = ActionVectorEntry.NonRepeatingShuttleLinkHeadCode + " at " + Utilities->Format96HHMM
15918  (GetRepeatTime(17, ActionVectorEntry.EventTime, y, IncMinutes));
15919  }
15920  else if((ActionVectorEntry.Command == "Fns-sh") && (y < (TrainDataEntry.NumberOfTrains - 1)))
15921  {
15922  PartStr = "At " + ActionVectorEntry.LocationName + " forms new service ";
15923  TimeStr = GetRepeatHeadCode(41, ActionVectorEntry.OtherHeadCode, y + 1, IncDigits) + " at " +
15924  Utilities->Format96HHMM(GetRepeatTime(14, ActionVectorEntry.EventTime, y, IncMinutes));
15925  // y+1 because it's the NEXT service repeat number that is relevant
15926  }
15927  else if((ActionVectorEntry.Command == "Fns-sh") && (y >= (TrainDataEntry.NumberOfTrains - 1)))
15928  {
15929  PartStr = "At " + ActionVectorEntry.LocationName + " forms new service";
15930  TimeStr = ActionVectorEntry.NonRepeatingShuttleLinkHeadCode + " at " + Utilities->Format96HHMM
15931  (GetRepeatTime(15, ActionVectorEntry.EventTime, y, IncMinutes));
15932  }
15933  else if((ActionVectorEntry.Command == "Frh-sh") && (y < (TrainDataEntry.NumberOfTrains - 1)))
15934  {
15935  PartStr = "At " + ActionVectorEntry.LocationName + " forms new service";
15936  TimeStr = GetRepeatHeadCode(43, ActionVectorEntry.OtherHeadCode, y + 1, IncDigits) + " at " +
15937  Utilities->Format96HHMM(GetRepeatTime(16, ActionVectorEntry.EventTime, y, IncMinutes));
15938  // y+1 because it's the NEXT service repeat number that is relevant
15939  }
15940  else if((ActionVectorEntry.Command == "Frh-sh") && (y >= (TrainDataEntry.NumberOfTrains - 1)))
15941  {
15942  PartStr = "Terminates shuttle service at " + ActionVectorEntry.LocationName;
15943  // only used in chronological tt
15944  TimeStr = "End at " + Utilities->Format96HHMM(GetRepeatTime(28, ActionVectorEntry.EventTime, y, IncMinutes));
15945  // the "End at " is stripped out of the chronological tt but displayed in the traditional tt
15946  }
15947  else if(ActionVectorEntry.Command == "Frh")
15948  {
15949  PartStr = "Terminates at " + ActionVectorEntry.LocationName;
15950  // need here to examine the time of the preceding entry, may be ArrivalTime if TimeLoc, or EventTime otherwise
15951  if(z > 0)
15952  // should be for finish entry but include check for safety
15953  {
15954  if(ActionVector.at(z - 1).EventTime != TDateTime(-1))
15955  {
15956  TimeStr = Utilities->Format96HHMM(GetRepeatTime(30, ActionVector.at(z - 1).EventTime, y, IncMinutes));
15957  }
15958  else if(ActionVector.at(z - 1).ArrivalTime != TDateTime(-1))
15959  {
15960  TimeStr = Utilities->Format96HHMM(GetRepeatTime(31, ActionVector.at(z - 1).ArrivalTime, y, IncMinutes));
15961  }
15962  else
15963  {
15964  TimeStr = " "; // shouldn't ever get here
15965  }
15966  }
15967  }
15968  else if(ActionVectorEntry.Command == "Fer")
15969  {
15970  AnsiString AllowedExits;
15971  PartStr = "Exits railway" + GetExitLocationAndAt(0, ActionVectorEntry.ExitList, AllowedExits) + AllowedExits;
15972  TimeStr = Utilities->Format96HHMM(GetRepeatTime(18, ActionVectorEntry.EventTime, y, IncMinutes));
15973  }
15974  else if(ActionVectorEntry.Command == "Fjo")
15975  {
15976  PartStr = "At " + ActionVectorEntry.LocationName + " joins";
15977  TimeStr = GetRepeatHeadCode(44, ActionVectorEntry.OtherHeadCode, y, IncDigits) + " at " +
15978  Utilities->Format96HHMM(GetRepeatTime(19, ActionVectorEntry.EventTime, y, IncMinutes));
15979  }
15980  }
15981  else if(ActionVectorEntry.SequenceType == SequTypeForRepeatEntry)
15982  {
15983  continue; // no entry needed for a repeat
15984  }
15985  OneTTEntry.Action = PartStr;
15986  OneTTEntry.Time = TimeStr;
15987  OneTTTrain.OneFormattedTrainVector.push_back(OneTTEntry);
15988  // one per action
15989  }
15990  OneTTLine.OneCompleteFormattedTrainVector.push_back(OneTTTrain);
15991  // one per repeat
15992  }
15993  AllTTTrains->push_back(OneTTLine); // one per repeating train
15994  }
15995  // AllTTTrains vector now complete
15996 
15997  std::ofstream TTFile(TTFileName.c_str()); //formatted timetable
15998 
15999  if(TTFile == 0)
16000  {
16001  StopTTClockMessage(64, "Formatted timetable file failed to open - can't be created");
16002  delete AllTTTrains;
16003  Utilities->CallLogPop(1567);
16004  return;
16005  }
16006 /* formatted timetable types
16007  class TOneTrainFormattedEntry
16008  {
16009  AnsiString Action;//includes location if relevant
16010  AnsiString Time;
16011  };
16012 
16013  typedef std::vector<TOneTrainFormattedEntry> TOneFormattedTrainVector;
16014 
16015  class TOneCompleteFormattedTrain//headcode + list of actions
16016  {
16017  public:
16018  AnsiString HeadCode;
16019  TOneFormattedTrainVector OneFormattedTrainVector;
16020  };
16021 
16022  typedef std::vector<TOneCompleteFormattedTrain> TOneCompleteFormattedTrainVector;//list af all repeats
16023 
16024  class TTrainFormattedInformation//contains all information for a single TT entry (including repeats)
16025  {
16026  public:
16027  AnsiString Header;//description, mass, power, brake rate etc
16028  int NumberOfTrains;// number of repeats + 1
16029  TOneCompleteFormattedTrainVector OneCompleteFormattedTrainVector;//list af all repeats
16030  };
16031 
16032  typedef std::vector<TTrainFormattedInformation> TAllFormattedTrains;//all timetable in formatted form
16033  //end of formatted timetable types
16034 */
16035 
16036  // new layout using multiple rows
16037  TTFile << TableTitle.c_str() << '\n' << '\n';
16038  for(unsigned int x = 0; x < AllTTTrains->size(); x++)
16039  {
16040  TTFile << AllTTTrains->at(x).Header.c_str();
16041  TTFile << '\n';
16042  TTFile << ','; // for the blank line above the action list
16043  for(int y = 0; y < AllTTTrains->at(x).NumberOfTrains; y++) // number of repeating trains
16044  {
16045  if(y < (AllTTTrains->at(x).NumberOfTrains - 1))
16046  {
16047  TTFile << AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(y).HeadCode.c_str() << ',';
16048  }
16049  else
16050  {
16051  TTFile << AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(y).HeadCode.c_str();
16052  }
16053  }
16054  TTFile << '\n' << '\n';
16055 
16056  for(unsigned int z = 0; z < AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(0).OneFormattedTrainVector.size(); z++)
16057  {
16058  TTFile << AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(0).OneFormattedTrainVector.at(z).Action.c_str() << ',';
16059  for(int y = 0; y < AllTTTrains->at(x).NumberOfTrains; y++) // number of repeating trains
16060  {
16061  if(y < (AllTTTrains->at(x).NumberOfTrains - 1))
16062  {
16063  TTFile << AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(y).OneFormattedTrainVector.at(z).Time.c_str() << ',';
16064  }
16065  else
16066  {
16067  TTFile << AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(y).OneFormattedTrainVector.at(z).Time.c_str();
16068  }
16069  }
16070  TTFile << '\n';
16071  }
16072  TTFile << '\n' << '\n';
16073  }
16074 
16075  TTFile.close();
16076 
16077  AnsiString TTFileName2 = TDateTime::CurrentDateTime().FormatString("dd-mm-yyyy hh.nn.ss");
16078 
16079  TTFileName2 = CurDir + "\\Formatted timetables\\Timetable " + TTFileName2 + "; " + RailwayTitle + "; " + TimetableTitle + ".txt";
16080 
16081  std::ofstream TTFile2(TTFileName2.c_str()); //chronological timetable
16082 
16083  if(TTFile2 == 0)
16084  {
16085  StopTTClockMessage(67, "Chronological timetable file failed to open - can't be created");
16086  delete AllTTTrains;
16087  Utilities->CallLogPop(1710);
16088  return;
16089  }
16090  typedef std::multimap<AnsiString, AnsiString>TAnsiMultiMap;
16091  std::multimap<AnsiString, AnsiString>::iterator AMMIT;
16092  std::pair<AnsiString, AnsiString>AnsiMultiMapEntry;
16093 
16094  TAnsiMultiMap *TAMM = new TAnsiMultiMap;
16095  LastTTTime = ""; //records the very last time in the timetable - used in analysis file for Frh entries
16096 
16097  // multimap of AnsiStrings with TimeString as key (to sort automatically)
16098 
16099  TTFile2 << TableTitle.c_str() << '\n' << '\n';
16100  for(unsigned int x = 0; x < AllTTTrains->size(); x++)
16101  {
16102  for(int y = 0; y < AllTTTrains->at(x).NumberOfTrains; y++) // number of repeating trains
16103  {
16104  for(unsigned int z = 0; z < AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(y).OneFormattedTrainVector.size(); z++)
16105  {
16106  bool GiveMessagesFalse = false;
16107  AnsiString TimeString = AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(y).OneFormattedTrainVector.at(z).Time;
16108  AnsiString HeadCodeString = AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(y).HeadCode;
16109  AnsiString ActionString = AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(y).OneFormattedTrainVector.at(z).Action;
16110  if(CheckHeadCodeValidity(11, GiveMessagesFalse, TimeString.SubString(1, 4)))
16111  // 'NXNN at HH:MM' (will return true if H/C as integ check passed)
16112  {
16113  // fails for HH:MM because of ':' or 'End at HH:MM' because of ' '
16114  AnsiString OtherHeadCode = TimeString.SubString(1, 4);
16115  TimeString = TimeString.SubString(9, 5);
16116  ActionString += " " + OtherHeadCode;
16117  }
16118  if(TimeString.SubString(1, 7) == "End at ")
16119  // for Frh-sh final entry
16120  {
16121  TimeString = TimeString.SubString(8, 5);
16122  }
16123  AnsiString OneLine = TimeString + ' ' + HeadCodeString + ' ' + ActionString + '\n';
16124  AnsiMultiMapEntry.first = TimeString;
16125  AnsiMultiMapEntry.second = OneLine;
16126  TAMM->insert(AnsiMultiMapEntry);
16127  }
16128  }
16129  }
16130 
16131  for(AMMIT = TAMM->begin(); AMMIT != TAMM->end(); AMMIT++)
16132  {
16133  TTFile2 << (AMMIT->second).c_str();
16134  }
16135  delete AllTTTrains;
16136  delete TAMM;
16137  TTFile2.close();
16138  Utilities->CallLogPop(1580);
16139 }
16140 
16141 // ---------------------------------------------------------------------------
16142 
16143 bool TTrainController::CreateTTAnalysisFile(int Caller, AnsiString RailwayTitle, AnsiString TimetableTitle, AnsiString CurDir, bool ArrChecked, bool DepChecked,
16144  bool AtLocChecked, bool DirChecked, int ArrRange, int DepRange)
16145 {
16146 
16147  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CreateTTAnalysisFile");
16148  bool AnalysisError = false;
16149  AnsiString SequenceLog = "SequenceLog\n";
16150 
16151 /* Double crosslink (shuttle) table:
16152 
16153 Command Format OtherHead NonRepeating- LinkTrain- NonRepeating- Decsription
16154  Code ShuttleLink- EntryPtr ShuttleLink-
16155  HeadCode EntryPtr
16156 
16157 Snt-sh SNTShuttle Y (rtn shuttle) N Y (rtn sh) N Simple shuttle - no feeder service
16158 Frh-sh TimeCmdHeadCode Y (outwd shuttle) N Y (outwd sh) N Simple shuttle - no finishing service
16159 F-nshs FNSNonRepeatToShuttle N (shld be Y for outwd shuttle) Y (shld be N) Y (correct) N (correct) Feeder service link to shuttle
16160 Sns-sh SNSShuttle Y (rtn shuttle) Y (feeder) Y (rtn) Y (fdr) Shuttle link from feeder service
16161 Sns-fsh SNSNonRepeatFromShuttle N (shld be Y for rtn shuttle) Y (shld be N) Y (correct) N (correct) Finishing service link from shuttle
16162 Fns-sh FSHNewService Y (outwd shuttle) Y (finishing) Y (outwd sh) Y (finish) Shuttle link to finishing service
16163 
16164 Note: Any shuttle start can have any finish - feeder and finish, neither, feeder but no finish & vice versa.
16165 */
16166 
16167  try
16168  {
16169  //New section at v2.5.0 for tt conflict analysis
16170  /*
16171  typedef std::list<AnsiString> TServiceCallingLocsList;
16172  typedef std::map<AnsiString, TServiceCallingLocsList> TAllServiceCallingLocsMap;
16173 
16175  struct TLocServiceTimes
16176  {
16177  AnsiString Location;
16178  AnsiString ServiceAndRepeatNum;
16179  AnsiString AtLocTime;
16180  AnsiString ArrTime;
16181  AnsiString DepTime;
16182  AnsiString FrhMarker;
16183  };
16184  typedef std::vector<TLocServiceTimes> TLocServiceTimesVector;
16185  */
16186 
16187  //first have to check through all the services and give each one a unique name, or the analysis won't recognise differences between services that have the same reference
16188  //to do that need a new TrainDataVector as don't want to change anything in the original. TrainDataVectorCopy is used for building AllServiceCallingLocsMap & LocServiceTimesVector
16189 
16190  TTrainDataVector TrainDataVectorCopy = TrainDataVector; //don't need it on heap as TrainController is on the heap. Didn't need others in CreatFormattedTimetables but leave as is.
16191  TTrainDataVector::iterator TDVIt, TDVCopyIt;
16192  int Suffix = 0;
16193  int IteratorNumber = 0;
16194  AnsiString AnsiSuffix = "";
16195  for(TDVIt = TrainDataVector.begin(); TDVIt != TrainDataVector.end() - 1; TDVIt++)
16196  {
16197  IteratorNumber++; //first value in loop is 1
16198  Suffix = 0;
16199  for(TDVCopyIt = TrainDataVectorCopy.begin() + IteratorNumber; TDVCopyIt != TrainDataVectorCopy.end(); TDVCopyIt++)
16200  {
16201  if(TDVCopyIt->ServiceReference == TDVIt->ServiceReference)
16202  {
16203  Suffix++; //first value is 1
16204  AnsiSuffix = AnsiString(Suffix);
16205  TDVCopyIt->ServiceReference = TDVIt->ServiceReference + "/" + AnsiSuffix;
16206  }
16207  }
16208  }
16209  SequenceLog += "1\n";
16210  //build AllServiceCallingLocsMap, it only uses the base service reference (with /1, /2 etc suffixes) as later times are calculated from the repeat number
16211  TServiceCallingLocsList ServiceCallingLocsList;
16212  std::pair<AnsiString, TServiceCallingLocsList> AllServiceCallingLocsEntry;
16213  for(unsigned int x = 0; x < TrainDataVectorCopy.size(); x++)
16214  {
16215  const TTrainDataEntry &TrainDataEntry = TrainDataVectorCopy.at(x);
16216  const TActionVector &ActionVector = TrainDataEntry.ActionVector;
16217  AllServiceCallingLocsEntry.first = TrainDataEntry.ServiceReference;
16218  ServiceCallingLocsList.clear();
16219  if(ActionVector.empty())
16220  {
16221  continue;
16222  }
16223  if(ActionVector.at(0).SignallerControl)
16224  {
16225  continue;
16226  }
16227  for(unsigned int z = 0; z < ActionVector.size(); z++)
16228  {
16229  TActionVectorEntry AVE = ActionVector.at(z);
16230  if(AVE.FormatType == StartNew)
16231  {
16232  if(AVE.LocationType == AtLocation) //located Snt
16233  {
16234  ServiceCallingLocsList.push_back(AVE.LocationName);
16235  }
16236  else //unlocated Snt (could be entering at continuation)
16237  {
16239  if(TE.ActiveTrackElementName != "")
16240  {
16241  ServiceCallingLocsList.push_back(TE.ActiveTrackElementName);
16242  }
16243  else
16244  {
16245  int HLoc = TE.HLoc;
16246  int VLoc = TE.VLoc;
16247  AnsiString HString;
16248  AnsiString VString;
16249  if(HLoc < 0)
16250  {
16251  HString = AnsiString('N') + AnsiString(HLoc).SubString(2, AnsiString(HLoc).Length() - 1); //strip off '-'
16252  }
16253  else
16254  {
16255  HString = AnsiString(HLoc);
16256  }
16257  if(VLoc < 0)
16258  {
16259  VString = AnsiString('N') + AnsiString(VLoc).SubString(2, AnsiString(VLoc).Length() - 1); //strip off '-'
16260  }
16261  else
16262  {
16263  VString = AnsiString(VLoc);
16264  }
16265  ServiceCallingLocsList.push_back(HString + '-' + VString);
16266  }
16267  }
16268  }
16269  else if(AVE.SequenceType == Start) //other start entries, all located
16270  {
16271  ServiceCallingLocsList.push_back(AVE.LocationName);
16272  }
16273  else if(AVE.FormatType == TimeLoc) //z must be > 0
16274  {
16275  if(ServiceCallingLocsList.back() != AVE.LocationName)
16276  {
16277  ServiceCallingLocsList.push_back(AVE.LocationName); //may be listed twice in succession so only want one entry
16278  }
16279  }
16280  else if(AVE.FormatType == PassTime)
16281  {
16282  ServiceCallingLocsList.push_back(AVE.LocationName);
16283  }
16284  else if(AVE.FormatType == TimeTimeLoc)
16285  {
16286  ServiceCallingLocsList.push_back(AVE.LocationName);
16287  }
16288  else if(AVE.Command == "cdt") //list if not next to start or finish
16289  {
16290  if(ActionVector.at(z-1).SequenceType == Start)
16291  {
16292  continue;
16293  }
16294  else if(ActionVector.at(z+1).SequenceType == Finish) //although deal with Fer entries cdt (train stopped) can't precede FER (train moving)
16295  {
16296  continue;
16297  }
16298  else
16299  {
16300  AnsiString TimeString = Utilities->Format96HHMM(AVE.EventTime);
16301  ServiceCallingLocsList.push_back("%%%" + TimeString); //%%% is a marker - unlikely that any locations will begin with this & easy to check to identify a time
16302  }
16303  }
16304  else if(AVE.FormatType == ExitRailway) //Fer
16305  {
16306  TTrackElement TE = Track->TrackElementAt(995, AVE.ExitList.front());
16307  AnsiString LName = TE.ActiveTrackElementName;
16308  if(LName != "")
16309  {
16310  ServiceCallingLocsList.push_back(LName);
16311  }
16312  else
16313  {
16314  int HLoc = TE.HLoc;
16315  int VLoc = TE.VLoc;
16316  AnsiString HString;
16317  AnsiString VString;
16318  if(HLoc < 0)
16319  {
16320  HString = AnsiString('N') + AnsiString(HLoc).SubString(2, AnsiString(HLoc).Length() - 1); //strip off '-'
16321  }
16322  else
16323  {
16324  HString = AnsiString(HLoc);
16325  }
16326  if(VLoc < 0)
16327  {
16328  VString = AnsiString('N') + AnsiString(VLoc).SubString(2, AnsiString(VLoc).Length() - 1); //strip off '-'
16329  }
16330  else
16331  {
16332  VString = AnsiString(VLoc);
16333  }
16334  ServiceCallingLocsList.push_back(HString + '-' + VString);
16335  }
16336  }
16337  }
16338  AllServiceCallingLocsEntry.second = ServiceCallingLocsList;
16339  AllServiceCallingLocsMap.insert(AllServiceCallingLocsEntry);
16340  }
16341  //AllServiceCallingLocsMap built
16342  SequenceLog += "2\n";
16343  //test validity of AllServiceCallingLocsMap
16344 /*
16345  AnsiString TestFile = CurDir + "\\Formatted timetables\\TestFile; " + RailwayTitle + "; " + TimetableTitle + ".txt";
16346  std::ofstream Test(TestFile.c_str());
16347 
16348  if(TestFile == 0)
16349  {
16350  ShowMessage("TestFile failed to open - can't be created");
16351  Utilities->CallLogPop();
16352  return false;
16353  }
16354 
16355  for(TAllServiceCallingLocsMap::iterator ASCLIt = AllServiceCallingLocsMap.begin(); ASCLIt != AllServiceCallingLocsMap.end(); ASCLIt++)
16356  {
16357  Test << ASCLIt->first << '\n'; //service ref
16358  for(TServiceCallingLocsList::iterator SCLIt = ASCLIt->second.begin(); SCLIt != ASCLIt->second.end(); SCLIt++)
16359  {
16360  Test << *SCLIt << '\n';
16361  }
16362  Test << "\n\n";
16363  }
16364  Test.close();
16365  Utilities->CallLogPop();
16366  return true;
16367 */
16368 
16369  //initialise variables before calc LastTTTime & build LocServiceTimesVector
16370  if(TrainDataVector.empty())
16371  {
16372  ShowMessage("Unable to create a program-readable timetable - please check the timetable file validity");
16373  Utilities->CallLogPop(2209);
16374  return(false);
16375  }
16376  TLocServiceTimes TLSTEntry;
16377  TLocServiceTimesVector LocServiceTimesVector; //will be on heap as TrainController is on the heap
16378  bool NumPlatsAtThisLocCalculated = false, ArrivalsPrinted = false, DeparturesPrinted = false, AtLocsPrinted = false;
16379  AnsiString PreviousService = "", PreviousServiceAndRepeatNumTotalOutput = "", BasicTime = "", MinuteString = "", LastAnsiTime = "";
16380  int NumTrains = 0, NumPlats = 0, LastFrhCount = 0, FrhCount = 0, NumTrainsAtLoc = 0;
16381  LastTTTime = "";
16382  SequenceLog += "3\n";
16383  //calculate LastTTTime
16384  for(unsigned int x = 0; x < TrainDataVectorCopy.size(); x++)
16385  {
16386  TTrainDataEntry &TrainDataEntry = TrainDataVectorCopy.at(x);
16387  TActionVector &ActionVector = TrainDataEntry.ActionVector;
16388  TActionVectorIterator AVLast = ActionVector.end() - 1; //points to last entry
16389  TDateTime LastTDTime;
16390  int IncMinutes = 0;
16391  NumTrains = TrainDataEntry.NumberOfTrains;
16392  if(ActionVector.empty())
16393  {
16394  continue;
16395  }
16396  if(ActionVector.at(0).SignallerControl)
16397  {
16398  continue;
16399  }
16400  if(AVLast->FormatType == Repeat)
16401  {
16402  IncMinutes = ActionVector.at(ActionVector.size() - 1).RearStartOrRepeatMins;
16403  AVLast--; //now points to the command before the repeat
16404  }
16405  if(AVLast->FormatType == FinRemHere) //not 'else if' as may have both a repeat and an Frh
16406  {
16407  AVLast--; //points to last timed entry
16408  }
16409  //here AVLast points to last entry with a time
16410  if(AVLast->ArrivalTime != TDateTime(-1))
16411  {
16412  LastTDTime = AVLast->ArrivalTime;
16413  }
16414  else if(AVLast->EventTime != TDateTime(-1)) //can't be a departure time
16415  {
16416  LastTDTime = AVLast->EventTime;
16417  }
16418  else
16419  {
16420  continue; //shouldn't ever reach here but if do then skip this service
16421  }
16422  if(NumTrains == 1)
16423  {
16424  LastAnsiTime = Utilities->Format96HHMM(LastTDTime);
16425  }
16426  else
16427  {
16428  LastAnsiTime = Utilities->Format96HHMM(GetRepeatTime(59, LastTDTime, NumTrains - 1, IncMinutes));
16429  }
16430  if(LastAnsiTime > LastTTTime)
16431  {
16432  LastTTTime = LastAnsiTime;
16433  }
16434  }
16435  SequenceLog += "4\n";
16436 //build LocServiceTimesVector
16437 
16438 /*
16439  struct TLocServiceTimes
16440  {
16441  AnsiString Location;
16442  AnsiString ServiceAndRepeatNum;
16443  AnsiString AtLocTime;
16444  AnsiString ArrTime;
16445  AnsiString DepTime;
16446  AnsiString FrhMarker;
16447  };
16448  typedef std::vector<TLocServiceTimes> TLocServiceTimesVector;
16449 
16450 This works as follows:
16451 ServiceAndRepeatNum is taken from the TrainDataVector as it is the same for all actionvector entries
16452 Location is taken from ActionVectorEntry.LocationName if there is one, or from the H & V locations if not (e.g. at an unnamed Fer)
16453 AtLocTime is always entered either on its own or with ArrTime or DepTime as appropriate
16454 
16455 Every action for every train is examined and times entered as follows:-
16456 a) a located Snt: entry time becomes the AtLocTime, and all subsequent minutes entered too up to but not including a departure or a finish
16457 b) an unlocated Snt: entry time becomes DepTime
16458 c) all other start entries: entry time becomes AtLoc, and all subsequent minutes entered too up to but not including a departure or a finish
16459 d) TimeLoc Arr: entry time becomes ArrTime, and all subsequent minutes entered too up to but not including a departure or a finish
16460 e) TimeLoc Dep: entry time becomes DepTime, checks if DepTime same as earlier ArrTime and if so all times go in as one entry
16461 f) TimeTimeLoc: Arrival time entered as ArrTime, a check if Arr & Dep same and if so go in as one entry, else all minutes between entered as AtLocs then DepTime
16462 g) ExitRailway (Fer): check if located and use LocationName if so. else use H & V positions, time becomes AtLocTime
16463 h) Frh: use the earlier vector time as the AtLocTime and set FrhMarker, and enter all minutes to end of timetable as AtLocs
16464 i) Frh-sh: for the last train use time as AtLocTime, set FrhMarker, and enter all minutes to end of timetable as AtLocs
16465 j) all other finish entries (all link to another service) are ignored as will be listed for the linked service
16466 */
16467  for(unsigned int x = 0; x < TrainDataVectorCopy.size(); x++)
16468  {
16469  const TTrainDataEntry &TrainDataEntry = TrainDataVectorCopy.at(x);
16470  const TActionVector &ActionVector = TrainDataEntry.ActionVector;
16471  AnsiString ServiceRef = TrainDataEntry.ServiceReference;
16472  int IncMinutes = 0;
16473  NumTrains = TrainDataEntry.NumberOfTrains;
16474  if(ActionVector.empty())
16475  {
16476  continue;
16477  }
16478  if(ActionVector.at(0).SignallerControl)
16479  {
16480  continue;
16481  }
16482  if(ActionVector.at(ActionVector.size() - 1).FormatType == Repeat)
16483  {
16484  IncMinutes = ActionVector.at(ActionVector.size() - 1).RearStartOrRepeatMins;
16485  }
16486  for(int y = 0; y < NumTrains; y++) //y is the repeat number
16487  {
16488  if(NumTrains == 1)
16489  {
16490  TLSTEntry.ServiceAndRepeatNum = ServiceRef;
16491  }
16492  else if(y == 0)
16493  {
16494  TLSTEntry.ServiceAndRepeatNum = ServiceRef + " (First service)";
16495  }
16496  else
16497  {
16498  TLSTEntry.ServiceAndRepeatNum = ServiceRef + " (Repeat " + AnsiString(y) + ")";
16499  }
16500  for(unsigned int z = 0; z < ActionVector.size(); z++)
16501  {
16502  TActionVectorEntry AVE = ActionVector.at(z);
16503  TLSTEntry.AtLocTime = "";
16504  TLSTEntry.ArrTime = "";
16505  TLSTEntry.DepTime = "";
16506  TLSTEntry.Location = "";
16507  TLSTEntry.FrhMarker = "";
16508 
16509  if(AVE.FormatType == StartNew) //Snt only
16510  {
16511  if(AVE.LocationType == AtLocation) //located Snt, class time as AtLocTime
16512  {
16513  TLSTEntry.Location = AVE.LocationName;
16514  TLSTEntry.AtLocTime = Utilities->Format96HHMM(GetRepeatTime(58, AVE.EventTime, y, IncMinutes));
16515  LocServiceTimesVector.push_back(TLSTEntry);
16516 
16517  //now look forwards until find a departure or a Fns, Fns-sh etc & add in all the minutes up to but not including the dep or finish times
16518  AnsiString IncTime = "", FoundStopTime = ""; //these handled in later checks
16519  for(unsigned int a = z + 1; a < ActionVector.size(); a++)
16520  {
16521  if(ActionVector.at(a).FormatType == TimeLoc) //must be a departure
16522  {
16523  FoundStopTime = Utilities->Format96HHMM(GetRepeatTime(62, ActionVector.at(a).DepartureTime, y, IncMinutes));
16524  break;
16525  }
16526  if(ActionVector.at(a).SequenceType == Finish) //finish catered in a later test
16527  {
16528  FoundStopTime = Utilities->Format96HHMM(GetRepeatTime(63, ActionVector.at(a).EventTime, y, IncMinutes));
16529  break;
16530  }
16531  }
16532  if(FoundStopTime == "")
16533  {
16534  throw Exception("Failure to determine FoundStopTime for located Snt");
16535  }
16536  int WhileCount = 0;
16537  while(true)
16538  {
16539  //add minutes until reach FoundStopTime but don't add that time
16540  WhileCount++;
16541  IncTime = Utilities->IncrementAnsiTimeOneMinute(TLSTEntry.AtLocTime);
16542  TLSTEntry.AtLocTime = IncTime; //all entered times will be AtLocs
16543  TLSTEntry.DepTime = "";
16544  TLSTEntry.ArrTime = "";
16545  if(IncTime >= FoundStopTime) //don't add that time
16546  {
16547  break;
16548  }
16549  LocServiceTimesVector.push_back(TLSTEntry);
16550  if(WhileCount > 2000)
16551  {
16552  throw Exception("While loop failed to break in 2000 loops for located Snt");
16553  }
16554  }
16555  }
16556  else //unlocated Snt, use the EventTime as DepTime for this vector
16557  {
16559  if(TE.ActiveTrackElementName != "")
16560  {
16561  TLSTEntry.Location = TE.ActiveTrackElementName;
16562  }
16563  else
16564  {
16565  int HLoc = TE.HLoc;
16566  int VLoc = TE.VLoc;
16567  AnsiString HString;
16568  AnsiString VString;
16569  if(HLoc < 0)
16570  {
16571  HString = AnsiString('N') + AnsiString(HLoc).SubString(2, AnsiString(HLoc).Length() - 1); //strip off '-'
16572  }
16573  else
16574  {
16575  HString = AnsiString(HLoc);
16576  }
16577  if(VLoc < 0)
16578  {
16579  VString = AnsiString('N') + AnsiString(VLoc).SubString(2, AnsiString(VLoc).Length() - 1); //strip off '-'
16580  }
16581  else
16582  {
16583  VString = AnsiString(VLoc);
16584  }
16585  TLSTEntry.Location = HString + '-' + VString;
16586  }
16587  TLSTEntry.DepTime = Utilities->Format96HHMM(GetRepeatTime(49, AVE.EventTime, y, IncMinutes));
16588  TLSTEntry.AtLocTime = TLSTEntry.DepTime;
16589  LocServiceTimesVector.push_back(TLSTEntry);
16590  }
16591  }
16592 
16593  else if(AVE.SequenceType == Start) //other start entries, all located
16594  {
16595  TLSTEntry.Location = AVE.LocationName;
16596  TLSTEntry.AtLocTime = Utilities->Format96HHMM(GetRepeatTime(50, AVE.EventTime, y, IncMinutes));
16597  LocServiceTimesVector.push_back(TLSTEntry);
16598  //now look forwards until find a departure or a Fns, Fns-sh etc & add in all the minutes up to but not including the dep or finish times
16599  AnsiString IncTime = "", FoundStopTime = ""; //these handled in other checks
16600  for(unsigned int a = z + 1; a < ActionVector.size(); a++)
16601  {
16602  if(ActionVector.at(a).FormatType == TimeLoc) //must be a departure
16603  {
16604  FoundStopTime = Utilities->Format96HHMM(GetRepeatTime(64, ActionVector.at(a).DepartureTime, y, IncMinutes));
16605  break;
16606  }
16607  if(ActionVector.at(a).SequenceType == Finish) //finish catered in a later test
16608  {
16609  FoundStopTime = Utilities->Format96HHMM(GetRepeatTime(65, ActionVector.at(a).EventTime, y, IncMinutes));
16610  break;
16611  }
16612  }
16613  if(FoundStopTime == "")
16614  {
16615  throw Exception("Failure to determine FoundStopTime for SequenceType == Start");
16616  }
16617  int WhileCount = 0;
16618  while(true)
16619  {
16620  //add minutes until reach FoundStopTime but don't add that time
16621  WhileCount++;
16622  IncTime = Utilities->IncrementAnsiTimeOneMinute(TLSTEntry.AtLocTime);
16623  TLSTEntry.AtLocTime = IncTime; //all entered times will be AtLocs
16624  TLSTEntry.DepTime = "";
16625  TLSTEntry.ArrTime = "";
16626  if(IncTime >= FoundStopTime) //don't add that time
16627  {
16628  break;
16629  }
16630  LocServiceTimesVector.push_back(TLSTEntry);
16631  if(WhileCount > 2000)
16632  {
16633  throw Exception("While loop failed to break in 2000 loops for SequenceType == Start");
16634  }
16635  }
16636  }
16637 
16638  else if(AVE.FormatType == TimeLoc) //could be arr or dep, if arrival add in all mins to the departure or finish
16639  {
16640  TLSTEntry.Location = AVE.LocationName;
16641  if(AVE.ArrivalTime > TDateTime(-1)) //one or other set, not both, in this case arrival
16642  {
16643  bool SkipAddingMinutes = false;
16644  TLSTEntry.ArrTime = Utilities->Format96HHMM(GetRepeatTime(51, AVE.ArrivalTime, y, IncMinutes));
16645  TLSTEntry.AtLocTime = TLSTEntry.ArrTime;
16646  LocServiceTimesVector.push_back(TLSTEntry); //Arr and AtLoc added (may be popped if dep time found to be same at next TimeLoc)
16647  //now look forwards until find a departure or a Fns, Fns-sh etc & add in all the minutes up to but not including the dep or finish times
16648  AnsiString IncTime = "", FoundStopTime = ""; //these handled in other checks
16649  for(unsigned int a = z + 1; a < ActionVector.size(); a++)
16650  {
16651  if(ActionVector.at(a).FormatType == TimeLoc) //must be a departure
16652  {
16653  FoundStopTime = Utilities->Format96HHMM(GetRepeatTime(66, ActionVector.at(a).DepartureTime, y, IncMinutes));
16654  break;
16655  }
16656  if(ActionVector.at(a).SequenceType == Finish) //finish catered for in a later test
16657  {
16658  FoundStopTime = Utilities->Format96HHMM(GetRepeatTime(67, ActionVector.at(a).EventTime, y, IncMinutes));
16659  if((a <= (z + 2)) && (FoundStopTime == TLSTEntry.ArrTime) && ((ActionVector.at(a).LinkedTrainEntryPtr > 0) || (ActionVector.at(a).NonRepeatingShuttleLinkEntryPtr > 0)))
16660  //finish immediately after arrival at same time, and a forward linked service. Added at v2.6.0 to prevent two linked trains being listed at same location
16661  //at v2.10.0 changed (a == z + 1) to (a <= (z + 2)) as can have a cdt between, this allows for that
16662  {
16663  LocServiceTimesVector.pop_back(); //pop the entry as the linked train will be listed at the relevant time and don't want to list both
16664  SkipAddingMinutes = true;
16665  }
16666  break;
16667  }
16668  }
16669  if(FoundStopTime == "")
16670  {
16671  throw Exception("Failure to determine FoundStopTime for SequenceType == Start");
16672  }
16673  if(!SkipAddingMinutes)
16674  {
16675  int WhileCount = 0;
16676  while(true)
16677  {
16678  //add minutes until reach FoundStopTime but don't add that time
16679  WhileCount++;
16680  IncTime = Utilities->IncrementAnsiTimeOneMinute(TLSTEntry.AtLocTime);
16681  TLSTEntry.AtLocTime = IncTime; //all entered times will be AtLocs
16682  TLSTEntry.DepTime = "";
16683  TLSTEntry.ArrTime = "";
16684  if(IncTime >= FoundStopTime) //don't add that time
16685  {
16686  break;
16687  }
16688  LocServiceTimesVector.push_back(TLSTEntry);
16689  if(WhileCount > 2000)
16690  {
16691  throw Exception("While loop failed to break in 2000 loops for SequenceType == Start");
16692  }
16693  }
16694  }
16695  }
16696  else if(AVE.DepartureTime > TDateTime(-1)) //need to check if the arrival time (which should already be listed) is same and if so put all times on one line
16697  {
16698  TLSTEntry.DepTime = Utilities->Format96HHMM(GetRepeatTime(52, AVE.DepartureTime, y, IncMinutes));
16699  TLSTEntry.AtLocTime = TLSTEntry.DepTime;
16700  if((TLSTEntry.Location == LocServiceTimesVector.back().Location) && (TLSTEntry.ServiceAndRepeatNum == LocServiceTimesVector.back().ServiceAndRepeatNum)) //if not it's a new service
16701  {
16702  if(TLSTEntry.DepTime == LocServiceTimesVector.back().ArrTime)
16703  {
16704  TLSTEntry.ArrTime = LocServiceTimesVector.back().ArrTime;
16705  LocServiceTimesVector.pop_back();
16706  LocServiceTimesVector.push_back(TLSTEntry); //Arr, Dep and AtLoc added in place of earlier Arr entry.
16707  }
16708  else //just add the dep & atloc times
16709  {
16710  TLSTEntry.ArrTime = "";
16711  LocServiceTimesVector.push_back(TLSTEntry);
16712  }
16713  }
16714  else //just add the dep & atloc times
16715  {
16716  TLSTEntry.ArrTime = "";
16717  LocServiceTimesVector.push_back(TLSTEntry);
16718  }
16719  }
16720  }
16721 
16722  else if(AVE.FormatType == TimeTimeLoc)
16723  {
16724  TLSTEntry.Location = AVE.LocationName;
16725  if(AVE.ArrivalTime > TDateTime(-1)) //should be
16726  {
16727  TLSTEntry.ArrTime = Utilities->Format96HHMM(GetRepeatTime(53, AVE.ArrivalTime, y, IncMinutes));
16728  TLSTEntry.AtLocTime = TLSTEntry.ArrTime;
16729  }
16730  if(AVE.DepartureTime > TDateTime(-1)) //should be
16731  {
16732  TLSTEntry.DepTime = Utilities->Format96HHMM(GetRepeatTime(54, AVE.DepartureTime, y, IncMinutes));
16733  }
16734  if(TLSTEntry.ArrTime == TLSTEntry.DepTime)
16735  {
16736  LocServiceTimesVector.push_back(TLSTEntry);
16737  }
16738  else
16739  {
16740  AnsiString TempDepTime = TLSTEntry.DepTime; //save it temporarily
16741  TLSTEntry.DepTime = "";
16742  LocServiceTimesVector.push_back(TLSTEntry); //push just the arrival and AtLoc times
16743  TLSTEntry.ArrTime = ""; //done with this now
16744  while(TLSTEntry.AtLocTime < TempDepTime)
16745  {
16746  TLSTEntry.AtLocTime = Utilities->IncrementAnsiTimeOneMinute(TLSTEntry.AtLocTime);
16747  if(TLSTEntry.AtLocTime == TempDepTime)
16748  {
16749  TLSTEntry.DepTime = TempDepTime; //restore value
16750  LocServiceTimesVector.push_back(TLSTEntry); //push the AtLoc and Dep times - will finish loop after this
16751  }
16752  else
16753  {
16754  LocServiceTimesVector.push_back(TLSTEntry); //push the AtLoc time on its own
16755  }
16756  }
16757  }
16758  }
16759 
16760  else if(AVE.FormatType == PassTime) //added at v2.9.1
16761  { //adds 2 entries, 1st with PassTime as ArrTime and AtLocTime, 2nd with PassTime as AtLocTime & DepTime
16762  TLSTEntry.Location = AVE.LocationName;;
16763  TLSTEntry.AtLocTime = Utilities->Format96HHMM(GetRepeatTime(73, AVE.EventTime, y, IncMinutes));
16764  TLSTEntry.ArrTime = TLSTEntry.AtLocTime; //DepTime already set to null
16765  LocServiceTimesVector.push_back(TLSTEntry); //1st entry
16766  TLSTEntry.ArrTime = ""; //need to reset this to null
16767  TLSTEntry.DepTime = TLSTEntry.AtLocTime;
16768  LocServiceTimesVector.push_back(TLSTEntry); //2nd entry
16769  }
16770 
16771  else if(AVE.FormatType == ExitRailway) //Fer
16772  {
16773  TLSTEntry.AtLocTime = Utilities->Format96HHMM(GetRepeatTime(55, AVE.EventTime, y, IncMinutes));
16774  //don't know which exit will be used during operation so use the first in ExitList, if several with different names then will
16775  //be wrong, but can't guess from here & most will have same name
16776  AnsiString LName = Track->TrackElementAt(990, AVE.ExitList.front()).ActiveTrackElementName;
16777  if(LName != "")
16778  {
16779  TLSTEntry.Location = LName;
16780  }
16781  else
16782  {
16783  int HLoc = Track->TrackElementAt(991, AVE.ExitList.front()).HLoc;
16784  int VLoc = Track->TrackElementAt(992, AVE.ExitList.front()).VLoc;
16785  AnsiString HString;
16786  AnsiString VString;
16787  if(HLoc < 0)
16788  {
16789  HString = AnsiString('N') + AnsiString(HLoc).SubString(2, AnsiString(HLoc).Length() - 1); //strip off '-'
16790  }
16791  else
16792  {
16793  HString = AnsiString(HLoc);
16794  }
16795  if(VLoc < 0)
16796  {
16797  VString = AnsiString('N') + AnsiString(VLoc).SubString(2, AnsiString(VLoc).Length() - 1); //strip off '-'
16798  }
16799  else
16800  {
16801  VString = AnsiString(VLoc);
16802  }
16803  TLSTEntry.Location = HString + '-' + VString;
16804  }
16805  LocServiceTimesVector.push_back(TLSTEntry); //just use the exit time as AtLocTime
16806  }
16807 
16808  else if(AVE.FormatType == FinRemHere) //Frh, not Frh-sh, that dealt with next
16809  {
16810  AnsiString FrhTime;
16811  if(ActionVector.at(z - 1).ArrivalTime != TDateTime(-1))
16812  {
16813  FrhTime = Utilities->Format96HHMM(GetRepeatTime(56, ActionVector.at(z - 1).ArrivalTime, y, IncMinutes));
16814  }
16815  else if(ActionVector.at(z - 1).EventTime != TDateTime(-1))
16816  {
16817  FrhTime = Utilities->Format96HHMM(GetRepeatTime(57, ActionVector.at(z - 1).EventTime, y, IncMinutes));
16818  }
16819  TLSTEntry.AtLocTime = FrhTime; //use the last entry time as the first recorded time
16820  TLSTEntry.Location = AVE.LocationName;
16821  AnsiString IncTime = Utilities->IncrementAnsiTimeOneMinute(TLSTEntry.AtLocTime);
16822  TLSTEntry.FrhMarker = "Frh";
16823  LocServiceTimesVector.push_back(TLSTEntry);
16824  TLSTEntry.FrhMarker = "";
16825  //add all times from next minute to end of timetable
16826  while(IncTime <= LastTTTime)
16827  {
16828  TLSTEntry.AtLocTime = IncTime;
16829  LocServiceTimesVector.push_back(TLSTEntry);
16830  IncTime = Utilities->IncrementAnsiTimeOneMinute(IncTime);
16831  }
16832  }
16833 
16834  else if(AVE.Command == "Frh-sh") //do nothing if links to other shuttle but treat as Frh when remaining here
16835  {
16836  if(y == NumTrains - 1) //last repeat, it remains here when accessed for the last train
16837  {
16838  TLSTEntry.AtLocTime = Utilities->Format96HHMM(GetRepeatTime(68, AVE.EventTime, y, IncMinutes));
16839  TLSTEntry.Location = AVE.LocationName;
16840  AnsiString IncTime = Utilities->IncrementAnsiTimeOneMinute(TLSTEntry.AtLocTime);
16841  TLSTEntry.FrhMarker = "Frh";
16842  LocServiceTimesVector.push_back(TLSTEntry);
16843  TLSTEntry.FrhMarker = "";
16844  //add all times from next minute to end of timetable
16845  while(IncTime <= LastTTTime)
16846  {
16847  TLSTEntry.AtLocTime = IncTime;
16848  LocServiceTimesVector.push_back(TLSTEntry);
16849  IncTime = Utilities->IncrementAnsiTimeOneMinute(IncTime);
16850  }
16851  }
16852  }
16853 
16854  else if(AVE.SequenceType == Finish) //other finish types - all located & all link to another service
16855  {
16856  //nothing is done here as the entry will be listed at this time under the new service reference
16857  }
16858  }
16859  }
16860  }
16861  SequenceLog += "5\n";
16862  //now sort in location order
16863  std::sort(LocServiceTimesVector.begin(), LocServiceTimesVector.end(), &LocServiceTimesLocationSort); //LocServiceTimesLocationSort is a function pointer
16864  //LocServiceTimesVector now complete & sorted in location order
16865 
16867 /*
16868  std::ofstream LSTVFile("LSTVFile.txt");
16869  for(TLocServiceTimesVector::iterator LSTVIt = LocServiceTimesVector.begin(); LSTVIt != LocServiceTimesVector.end(); LSTVIt++)
16870  {
16871  LSTVFile << LSTVIt->Location + '\n';
16872  LSTVFile << LSTVIt->ServiceAndRepeatNum + '\n';
16873  LSTVFile << "AtLocTime = " + LSTVIt->AtLocTime + '\n';
16874  LSTVFile << "ArrTime = " + LSTVIt->ArrTime + '\n';
16875  LSTVFile << "DepTime = " + LSTVIt->DepTime + '\n';
16876  if(LSTVIt->FrhMarker == "")
16877  {
16878  LSTVFile << "Not Frh\n";
16879  }
16880  else
16881  {
16882  LSTVFile << LSTVIt->FrhMarker + '\n';
16883  }
16884  LSTVFile << '\n';
16885  }
16886  LSTVFile.close();
16887 */
16888  //declare pointers for use in printouts
16889  TLocServiceTimesVector::iterator Ptr1, Ptr2;
16890 
16891  //set up the output file
16892  AnsiString TTFileName3 = TDateTime::CurrentDateTime().FormatString("dd-mm-yyyy hh.nn.ss");
16893  TTFileName3 = CurDir + "\\Formatted timetables\\Conflict Analysis " + TTFileName3 + "; " + RailwayTitle + "; " + TimetableTitle + ".csv";
16894 
16895  std::ofstream TTFile3(TTFileName3.c_str());
16896 
16897  if(TTFile3 == 0)
16898  {
16899  ShowMessage("Conflict Analysis file failed to open - can't be created");
16900  Utilities->CallLogPop(2210);
16901  return(false);
16902  }
16903  if(LocServiceTimesVector.empty())
16904  {
16905  ShowMessage("No timetabled services found");
16906  TTFile3.close();
16907  DeleteFile(TTFileName3);
16908  Utilities->CallLogPop(2211);
16909  return(false);
16910  }
16911  TTFile3 << "Timetable analysis for timetable: '" + TimetableTitle + "' in conjunction with railway: '" + RailwayTitle + "'\n";
16912  TTFile3 << "See user manual or on-screen help section 5.12 for detailed information.\n\n\n";
16913  SequenceLog += "6\n";
16914 
16915 /* print out TrainDataVectorCopy for debugging purposes
16916 std::ofstream TDVCFile("TDVCFile.txt");
16917 for(TTrainDataVector::iterator TDVCIt = TrainDataVectorCopy.begin(); TDVCIt != TrainDataVectorCopy.end(); TDVCIt++)
16918 {
16919  TDVCFile << TDVCIt->ServiceReference + '\n';
16920  TDVCFile << TDVCIt->Description + '\n';
16921  for(unsigned int x = 0; x < TDVCIt->ActionVector.size(); x++)
16922  {
16923  TActionVectorEntry AVE = TDVCIt->ActionVector.at(x);
16924  if((AVE.FormatType == TimeCmd) || (AVE.FormatType == TimeCmdHeadCode) || (AVE.FormatType == FNSNonRepeatToShuttle) || (AVE.FormatType == FSHNewService))
16925  {
16926  TDVCFile << AnsiString(AVE.EventTime.TimeString()) << ' ' << AVE.Command << ' ' << AVE.OtherHeadCode << ' ' << AVE.NonRepeatingShuttleLinkHeadCode << '\n';
16927  }
16928  else if((AVE.FormatType == TimeLoc) && (AVE.ArrivalTime != TDateTime(-1)))
16929  {
16930  TDVCFile << AnsiString(AVE.ArrivalTime.TimeString()) << " Arr " << AVE.LocationName << '\n';
16931  }
16932  else if((AVE.FormatType == TimeLoc) && (AVE.DepartureTime != TDateTime(-1)))
16933  {
16934  TDVCFile << AnsiString(AVE.DepartureTime.TimeString()) << " Dep " << AVE.LocationName << '\n';
16935  }
16936  else if(AVE.FormatType == TimeTimeLoc)
16937  {
16938  TDVCFile << AnsiString(AVE.ArrivalTime.TimeString()) << ' ' << AnsiString(AVE.DepartureTime.TimeString()) << ' ' << AVE.LocationName << '\n';
16939  }
16940  else if(AVE.FormatType == PassTime)
16941  {
16942  TDVCFile << AnsiString(AVE.EventTime.TimeString()) << ' ' << "Pass" << ' ' << AVE.LocationName << '\n';
16943  }
16944  else if(AVE.FormatType == ExitRailway)
16945  {
16946  TDVCFile << AnsiString(AVE.EventTime.TimeString()) << " Fer" << '\n';
16947  }
16948  else if(AVE.FormatType == FinRemHere)
16949  {
16950  TDVCFile << "Frh" << '\n';
16951  }
16952  }
16953  TDVCFile << '\n';
16954 }
16955 TDVCFile.close();
16956 
16957 */
16958  //arrivals
16959  if(ArrChecked)
16960  {
16961  //sort in ArrTime order for each location
16962  Ptr1 = LocServiceTimesVector.begin();
16963  Ptr2 = Ptr1 + 1;
16964  while(Ptr2 != LocServiceTimesVector.end())
16965  {
16966  while(Ptr2->Location == Ptr1->Location) //ends with Ptr2 one past same Location value as Ptr1
16967  {
16968  Ptr2++;
16969  if(Ptr2 == LocServiceTimesVector.end())
16970  {
16971  break;
16972  }
16973  }
16974  std::sort(Ptr1, Ptr2, &LocServiceTimesArrTimeSort);
16975  Ptr1 = Ptr2; //first entry with next name
16976  if(Ptr2 != LocServiceTimesVector.end())
16977  {
16978  Ptr2++;
16979  }
16980  }
16981 
16982  //routine for arrivals - number of trains arriving within the specified range with services listed at the end
16983 
16984  TTFile3 << "Arrival & pass analysis: an asterisk means that the number of same approach code arrivals and passes is equal to or greater than the number of platforms.\n";
16985  TTFile3 << "If the total number of arrivals and passes at the same time exceeds the number of platforms the 'Trains present at location analysis' will show an asterisk.\n\n";
16986  MinuteString = " minutes";
16987  AnsiString ServiceAndRepeatNumTotal = "", ServiceAndRepeatNumTotalOutput = "";
16988  if(ArrRange == 1)
16989  {
16990  MinuteString = " minute";
16991  }
16992  TTFile3 << "Location,Number of,Number of,Services arriving within " << AnsiString(ArrRange) << MinuteString << " with their arrival times and approach codes\n";
16993  TTFile3 << ",Platforms,Trains\n\n";
16994 
16995  Ptr1 = LocServiceTimesVector.begin();
16996  Ptr2 = Ptr1 + 1;
16997  while(Ptr2 != LocServiceTimesVector.end())
16998  {
16999  PreviousService = "";
17000  NumTrainsAtLoc = 0;
17001  ServiceAndRepeatNumTotal = "";
17002  NumPlats = 0;
17003  NumPlatsAtThisLocCalculated = false;
17004  BasicTime = "";
17005  while((Ptr2->Location != Ptr1->Location) || ((Ptr1->Location == "") && (Ptr2->Location == "")))
17006  {
17007  PreviousService = "";
17008  NumTrainsAtLoc = 0;
17009  ServiceAndRepeatNumTotal = "";
17010  NumPlats = 0;
17011  NumPlatsAtThisLocCalculated = false;
17012  BasicTime = "";
17013  Ptr1++;
17014  Ptr2++;
17015  if(Ptr2 == LocServiceTimesVector.end())
17016  {
17017  break;
17018  }
17019  }
17020  if(Ptr2 == LocServiceTimesVector.end())
17021  {
17022  break;
17023  }
17024  while(Ptr2->Location == Ptr1->Location)
17025  {
17026  PreviousService = "";
17027  NumTrainsAtLoc = 0;
17028  ServiceAndRepeatNumTotal = "";
17029  BasicTime = Ptr1->ArrTime; //used to compare later times - later pointer contents have same or later times as sorted in time order
17030  if((Ptr1->Location == "") && (Ptr2->Location == ""))
17031  {
17032  break;
17033  }
17034  while(!WithinTimeRange(0, BasicTime, Ptr2->ArrTime, ArrRange) || ((Ptr1->ArrTime == "") && (Ptr2->ArrTime == "")))
17035  {
17036  BasicTime = Ptr2->ArrTime; //used to compare later times or last can exceed first
17037  Ptr1++;
17038  Ptr2++;
17039  if(Ptr2 == LocServiceTimesVector.end())
17040  {
17041  break;
17042  }
17043  if(Ptr2->Location != Ptr1->Location)
17044  {
17045  break;
17046  }
17047  }
17048  if(Ptr2 == LocServiceTimesVector.end())
17049  {
17050  break;
17051  }
17052  if(Ptr2->Location != Ptr1->Location)
17053  {
17054  break;
17055  }
17056  while(WithinTimeRange(1, BasicTime, Ptr2->ArrTime, ArrRange))
17057  {
17058  if((Ptr1->ArrTime == "") && (Ptr2->ArrTime == ""))
17059  {
17060  break;
17061  }
17062  if(!NumPlatsAtThisLocCalculated) //num plats at relevant location, reset when locations change
17063  {
17064  NumPlats = Track->NumberOfPlatforms(0, Ptr1->Location);
17065  NumPlatsAtThisLocCalculated = true;
17066  }
17067  if(Ptr1->ServiceAndRepeatNum != PreviousService) //don't print it twice if same as last - as will be if >1 service at same loc at same time
17068  {
17069  if(ServiceAndRepeatNumTotal == "")
17070  {
17071  ServiceAndRepeatNumTotal = Ptr1->ServiceAndRepeatNum + "," + Ptr1->ArrTime;
17072  NumTrainsAtLoc = 1;
17073  }
17074  else
17075  {
17076  ServiceAndRepeatNumTotal = ServiceAndRepeatNumTotal + "," + Ptr1->ServiceAndRepeatNum + "," + Ptr1->ArrTime;
17077  }
17078  }
17079  PreviousService = Ptr2->ServiceAndRepeatNum; //last service at relevant time, reset when times differ
17080  if(ServiceAndRepeatNumTotal == "")
17081  {
17082  ServiceAndRepeatNumTotal = Ptr2->ServiceAndRepeatNum + "," + Ptr2->ArrTime;
17083  NumTrainsAtLoc = 1;
17084  }
17085  else
17086  {
17087  ServiceAndRepeatNumTotal = ServiceAndRepeatNumTotal + "," + Ptr2->ServiceAndRepeatNum + "," + Ptr2->ArrTime;
17088  }
17089  Ptr1 = Ptr2;
17090  Ptr2++;
17091  if((Ptr2 == LocServiceTimesVector.end()) || (Ptr2->Location != Ptr1->Location) || (!WithinTimeRange(2, BasicTime, Ptr2->ArrTime, ArrRange)))
17092  {
17093  int MaxNumberOfSameDirections = 0;
17094  ServiceAndRepeatNumTotalOutput = ConsolidateSARNTArrDep(1, ServiceAndRepeatNumTotal, NumTrainsAtLoc, Ptr1->Location, true, AnalysisError, MaxNumberOfSameDirections); //sort into alphabetical order and remove duplicates
17095  if(AnalysisError) //has to be Ptr1->Location as Ptr2 loc may have changed
17096  {
17097 // ShowMessage("Error in arrival analysis - file will be incomplete and/or corrupt. Please send railway and timetable files to railwayfeedback@gmail.com for investigation - thanks. Details: " + ServiceAndRepeatNumTotalOutput);
17098  TTFile3.close();
17099  throw Exception(ServiceAndRepeatNumTotalOutput.c_str());
17100 // Utilities->CallLogPop(2224);
17101 // return false;
17102  }
17103  AnsiString Asterisk = "";
17104  if(MaxNumberOfSameDirections >= NumPlats)
17105  {
17106  Asterisk = "* ";
17107  }
17108  //print out a single line for number of trains at loc with all service refs
17109  TTFile3 << Asterisk << Ptr1->Location << "," << NumPlats << "," << NumTrainsAtLoc << "," << ServiceAndRepeatNumTotalOutput << '\n'; //no description as >1 service
17110  ArrivalsPrinted = true;
17111  ServiceAndRepeatNumTotal = "";
17112  }
17113  if(Ptr2 == LocServiceTimesVector.end())
17114  {
17115  break;
17116  }
17117  if(Ptr2->Location != Ptr1->Location)
17118  {
17119  break;
17120  }
17121  }
17122  if(Ptr2 == LocServiceTimesVector.end())
17123  {
17124  break;
17125  }
17126  }
17127  }
17128  if(!ArrivalsPrinted)
17129  {
17130  TTFile3 << "Nothing to report for arrivals";
17131  }
17132  TTFile3 << "\n\n";
17133  }
17134  //end of routine for arrivals
17135  SequenceLog += "7\n";
17136  //departures
17137  if(DepChecked)
17138  {
17139  //sort in DepTime order for each location
17140  Ptr1 = LocServiceTimesVector.begin();
17141  Ptr2 = Ptr1 + 1;
17142  while(Ptr2 != LocServiceTimesVector.end())
17143  {
17144  while(Ptr2->Location == Ptr1->Location) //ends with Ptr2 one past same Location value as Ptr1
17145  {
17146  Ptr2++;
17147  if(Ptr2 == LocServiceTimesVector.end())
17148  {
17149  break;
17150  }
17151  }
17152  std::sort(Ptr1, Ptr2, &LocServiceTimesDepTimeSort);
17153  Ptr1 = Ptr2; //first entry with next name
17154  if(Ptr2 != LocServiceTimesVector.end())
17155  {
17156  Ptr2++;
17157  }
17158  }
17159 
17160  //routine for departures - number of trains departing within the specified range with services listed at the end
17161  TTFile3 << "Departure & pass analysis: an asterisk means that the number of same exit code departures and passes is equal to or greater than the number of platforms.\n";
17162  TTFile3 << "If the total number of departures and passes at the same time exceeds the number of platforms the 'Trains present at location analysis' will show an asterisk.\n\n";
17163  MinuteString = " minutes";
17164  AnsiString ServiceAndRepeatNumTotal = "", ServiceAndRepeatNumTotalOutput = "";
17165  if(DepRange == 1)
17166  {
17167  MinuteString = " minute";
17168  }
17169  TTFile3 << "Location,Number of,Number of,Services departing within " << AnsiString(DepRange) << MinuteString << " with their departure times and exit codes\n";
17170  TTFile3 << ",Platforms,Trains\n\n";
17171 
17172  Ptr1 = LocServiceTimesVector.begin();
17173  Ptr2 = Ptr1 + 1;
17174  while(Ptr2 != LocServiceTimesVector.end())
17175  {
17176  PreviousService = "";
17177  NumTrainsAtLoc = 0;
17178  ServiceAndRepeatNumTotal = "";
17179  NumPlats = 0;
17180  NumPlatsAtThisLocCalculated = false;
17181  BasicTime = "";
17182  while((Ptr2->Location != Ptr1->Location) || ((Ptr1->Location == "") && (Ptr2->Location == "")))
17183  {
17184  PreviousService = "";
17185  NumTrainsAtLoc = 0;
17186  ServiceAndRepeatNumTotal = "";
17187  NumPlats = 0;
17188  NumPlatsAtThisLocCalculated = false;
17189  BasicTime = "";
17190  Ptr1++;
17191  Ptr2++;
17192  if(Ptr2 == LocServiceTimesVector.end())
17193  {
17194  break;
17195  }
17196  }
17197  if(Ptr2 == LocServiceTimesVector.end())
17198  {
17199  break;
17200  }
17201  while(Ptr2->Location == Ptr1->Location)
17202  {
17203  PreviousService = "";
17204  NumTrainsAtLoc = 0;
17205  ServiceAndRepeatNumTotal = "";
17206  BasicTime = Ptr1->DepTime; //used to compare later times - later pointer contents have same or later times as sorted in time order
17207  if((Ptr1->Location == "") && (Ptr2->Location == ""))
17208  {
17209  break;
17210  }
17211  while(!WithinTimeRange(3, BasicTime, Ptr2->DepTime, DepRange) || ((Ptr1->DepTime == "") && (Ptr2->DepTime == "")))
17212  {
17213  BasicTime = Ptr2->DepTime; //used to compare later times or last can exceed first
17214  Ptr1++;
17215  Ptr2++;
17216  if(Ptr2 == LocServiceTimesVector.end())
17217  {
17218  break;
17219  }
17220  if(Ptr2->Location != Ptr1->Location)
17221  {
17222  break;
17223  }
17224  }
17225  if(Ptr2 == LocServiceTimesVector.end())
17226  {
17227  break;
17228  }
17229  if(Ptr2->Location != Ptr1->Location)
17230  {
17231  break;
17232  }
17233  while(WithinTimeRange(4, BasicTime, Ptr2->DepTime, DepRange))
17234  {
17235  if((Ptr1->DepTime == "") && (Ptr2->DepTime == ""))
17236  {
17237  break;
17238  }
17239  if(!NumPlatsAtThisLocCalculated) //num plats at relevant location, reset when locations change
17240  {
17241  NumPlats = Track->NumberOfPlatforms(1, Ptr1->Location);
17242  NumPlatsAtThisLocCalculated = true;
17243  }
17244  if(Ptr1->ServiceAndRepeatNum != PreviousService) //don't print it twice if same as last - as will be if >1 service at same loc at same time
17245  {
17246  if(ServiceAndRepeatNumTotal == "")
17247  {
17248  ServiceAndRepeatNumTotal = Ptr1->ServiceAndRepeatNum + "," + Ptr1->DepTime;
17249  NumTrainsAtLoc = 1;
17250  }
17251  else
17252  {
17253  ServiceAndRepeatNumTotal = ServiceAndRepeatNumTotal + "," + Ptr1->ServiceAndRepeatNum + "," + Ptr1->DepTime;
17254  }
17255  }
17256  PreviousService = Ptr2->ServiceAndRepeatNum; //last service at relevant time, reset when times differ
17257  if(ServiceAndRepeatNumTotal == "")
17258  {
17259  ServiceAndRepeatNumTotal = Ptr2->ServiceAndRepeatNum + "," + Ptr2->DepTime;
17260  NumTrainsAtLoc = 1;
17261  }
17262  else
17263  {
17264  ServiceAndRepeatNumTotal = ServiceAndRepeatNumTotal + "," + Ptr2->ServiceAndRepeatNum + "," + Ptr2->DepTime;
17265  }
17266  Ptr1 = Ptr2;
17267  Ptr2++;
17268  if((Ptr2 == LocServiceTimesVector.end()) || (Ptr2->Location != Ptr1->Location) || (!WithinTimeRange(5, BasicTime, Ptr2->DepTime, DepRange)))
17269  {
17270  int MaxNumberOfSameDirections = 0;
17271  ServiceAndRepeatNumTotalOutput = ConsolidateSARNTArrDep(3, ServiceAndRepeatNumTotal, NumTrainsAtLoc, Ptr1->Location, false, AnalysisError, MaxNumberOfSameDirections); //sort into alphabetical order and remove duplicates
17272  if(AnalysisError) //has to be Ptr1->Location as Ptr2 loc may have changed
17273  {
17274 // ShowMessage("Error in departure analysis - file will be incomplete and/or corrupt. Please send railway and timetable files to railwayfeedback@gmail.com for investigation - thanks. Details: " + ServiceAndRepeatNumTotalOutput);
17275  TTFile3.close();
17276  throw Exception(ServiceAndRepeatNumTotalOutput.c_str());
17277 // Utilities->CallLogPop(2225);
17278 // return false;
17279  }
17280  AnsiString Asterisk = "";
17281  if(MaxNumberOfSameDirections >= NumPlats)
17282  {
17283  Asterisk = "* ";
17284  }
17285  //print out a single line for number of trains at loc with all service refs
17286  TTFile3 << Asterisk << Ptr1->Location << "," << NumPlats << "," << NumTrainsAtLoc << "," << ServiceAndRepeatNumTotalOutput << '\n'; //no description as >1 service
17287  DeparturesPrinted = true;
17288  ServiceAndRepeatNumTotal = "";
17289  }
17290  if(Ptr2 == LocServiceTimesVector.end())
17291  {
17292  break;
17293  }
17294  if(Ptr2->Location != Ptr1->Location)
17295  {
17296  break;
17297  }
17298  }
17299  if(Ptr2 == LocServiceTimesVector.end())
17300  {
17301  break;
17302  }
17303  }
17304  }
17305  if(!DeparturesPrinted)
17306  {
17307  TTFile3 << "Nothing to report for departures";
17308  }
17309  TTFile3 << "\n\n";
17310  }
17311  //end of routine for departures
17312  SequenceLog += "8\n";
17313 
17314  //list trains at locations at same time
17315 
17316  if(AtLocChecked)
17317  {
17318  //sort in AtLocTime order for each location
17319  Ptr1 = LocServiceTimesVector.begin();
17320  Ptr2 = Ptr1 + 1;
17321  while(Ptr2 != LocServiceTimesVector.end())
17322  {
17323  while(Ptr2->Location == Ptr1->Location) //ends with Ptr2 one past same Location value as Ptr1
17324  {
17325  Ptr2++;
17326  if(Ptr2 == LocServiceTimesVector.end())
17327  {
17328  break;
17329  }
17330  }
17331  std::sort(Ptr1, Ptr2, &LocServiceTimesAtLocTimeSort);
17332  Ptr1 = Ptr2; //first entry with next name
17333  if(Ptr2 != LocServiceTimesVector.end())
17334  {
17335  Ptr2++;
17336  }
17337  }
17338 
17339  //print out simultaneous AtLocs (don't need range of times for AtLocs)
17340  TTFile3 << "Trains present at location analysis: an asterisk means that the number of trains at the location is greater than the number of platforms.\n\n";
17341  TTFile3 << "Location,Number of,Number of,Time,Services at the location at that time\n";
17342  TTFile3 << ",Platforms,Trains,\n\n";
17343  AnsiString ServiceAndRepeatNumTotal = "", ServiceAndRepeatNumTotalOutput = "";
17344  Ptr1 = LocServiceTimesVector.begin();
17345  Ptr2 = Ptr1 + 1;
17346  while(Ptr2 != LocServiceTimesVector.end())
17347  {
17348  PreviousService = "";
17349  ServiceAndRepeatNumTotal = "";
17350  NumTrainsAtLoc = 0;
17351  NumPlats = 0;
17352  NumPlatsAtThisLocCalculated = false;
17353  FrhCount = 0;
17354  while((Ptr2->Location != Ptr1->Location) || ((Ptr1->Location == "") && (Ptr2->Location == "")))
17355  {
17356  PreviousService = "";
17357  ServiceAndRepeatNumTotal = "";
17358  NumTrainsAtLoc = 0;
17359  NumPlats = 0;
17360  NumPlatsAtThisLocCalculated = false;
17361  FrhCount = 0;
17362  Ptr1++;
17363  Ptr2++;
17364  if(Ptr2 == LocServiceTimesVector.end())
17365  {
17366  break;
17367  }
17368  }
17369  if(Ptr2 == LocServiceTimesVector.end())
17370  {
17371  break;
17372  }
17373  while(Ptr2->Location == Ptr1->Location)
17374  {
17375  if(Ptr1->FrhMarker == "Frh") //this test is made here and each time Ptr1 increases with Ptr1 & 2 at same loc so as to catch them all
17376  {
17377  FrhCount++;
17378  Ptr1->FrhMarker = "FrhCounted"; //to avoid double counting
17379  }
17380  PreviousService = "";
17381  NumTrainsAtLoc = 0;
17382  ServiceAndRepeatNumTotal = "";
17383  if((Ptr1->Location == "") && (Ptr2->Location == ""))
17384  {
17385  break;
17386  }
17387  while((Ptr2->AtLocTime != Ptr1->AtLocTime) || ((Ptr1->AtLocTime == "") && (Ptr2->AtLocTime == "")))
17388  {
17389  Ptr1++;
17390  if(Ptr1->FrhMarker == "Frh")
17391  {
17392  FrhCount++;
17393  Ptr1->FrhMarker = "FrhCounted"; //to avoid double counting
17394  }
17395  Ptr2++;
17396  if(Ptr2 == LocServiceTimesVector.end())
17397  {
17398  break;
17399  }
17400  if(Ptr2->Location != Ptr1->Location)
17401  {
17402  break;
17403  }
17404  }
17405  if(Ptr2 == LocServiceTimesVector.end())
17406  {
17407  break;
17408  }
17409  if(Ptr2->Location != Ptr1->Location)
17410  {
17411  break;
17412  }
17413  while(Ptr2->AtLocTime == Ptr1->AtLocTime)
17414  {
17415  if((Ptr1->AtLocTime == "") && (Ptr2->AtLocTime == ""))
17416  {
17417  break;
17418  }
17419  if(!NumPlatsAtThisLocCalculated) //num plats at relevant location, reset when locations change
17420  {
17421  NumPlats = Track->NumberOfPlatforms(2, Ptr1->Location);
17422  NumPlatsAtThisLocCalculated = true;
17423  }
17424  if(Ptr1->ServiceAndRepeatNum != PreviousService) //don't print it twice if same as last - as will be if >1 service at same loc at same time
17425  {
17426  if(ServiceAndRepeatNumTotal == "")
17427  {
17428  ServiceAndRepeatNumTotal = Ptr1->ServiceAndRepeatNum;
17429  NumTrainsAtLoc = 1;
17430  }
17431  else
17432  {
17433  ServiceAndRepeatNumTotal = ServiceAndRepeatNumTotal + "," + Ptr1->ServiceAndRepeatNum;
17434  }
17435  }
17436  PreviousService = Ptr2->ServiceAndRepeatNum; //last service at relevant time, reset when times differ, has to be Ptr2 to compare Ptr1 at next round when incremented
17437  if(ServiceAndRepeatNumTotal == "")
17438  {
17439  ServiceAndRepeatNumTotal = Ptr2->ServiceAndRepeatNum;
17440  NumTrainsAtLoc = 1;
17441  }
17442  else
17443  {
17444  ServiceAndRepeatNumTotal = ServiceAndRepeatNumTotal + "," + Ptr2->ServiceAndRepeatNum;
17445  }
17446  Ptr1 = Ptr2;
17447  if(Ptr1->FrhMarker == "Frh")
17448  {
17449  FrhCount++;
17450  Ptr1->FrhMarker = "FrhCounted"; //to avoid double counting
17451  }
17452  Ptr2++;
17453  if((Ptr2 == LocServiceTimesVector.end()) || (Ptr2->Location != Ptr1->Location) || (Ptr2->AtLocTime != Ptr1->AtLocTime))
17454  {
17455 //old text //only print out if no remainers (1st condition), change in remainers (2nd condition) or change in ServiceAndRepeatNumTotalOutput, and >1 train (later condition)
17456 //new text //don't print out if all remainers or if only 1 train at loc
17457  ServiceAndRepeatNumTotalOutput = ConsolidateSARNTAtLoc(1, ServiceAndRepeatNumTotal, NumTrainsAtLoc); //sort into alphabetical order, remove duplicates, and calculate new value for NumTrainsAtLoc
17458 //old condits if((FrhCount == 0) || (FrhCount != LastFrhCount) || (PreviousServiceAndRepeatNumTotalOutput != ServiceAndRepeatNumTotalOutput))//don't print if same output
17459 /*new condits*/ if((NumTrainsAtLoc > 1) && ((FrhCount < NumTrainsAtLoc) || (FrhCount != LastFrhCount)))
17460  {
17461  AnsiString Asterisk = "";
17462  if(NumTrainsAtLoc > NumPlats)
17463  {
17464  Asterisk = "* ";
17465  }
17466  //print out a single line for number of trains at loc with all service refs
17467  if(FrhCount == 0)
17468  {
17469  TTFile3 << Asterisk << Ptr1->Location << "," << NumPlats << "," << NumTrainsAtLoc << "," << Ptr1->AtLocTime << "," << ServiceAndRepeatNumTotalOutput << '\n';
17470  }
17471  else if(FrhCount == 1)
17472  {
17473  TTFile3 << Asterisk << Ptr1->Location << "," << NumPlats << "," << NumTrainsAtLoc << "," << Ptr1->AtLocTime << " (1 remains here)," << ServiceAndRepeatNumTotalOutput << '\n';
17474  }
17475  else
17476  {
17477  TTFile3 << Asterisk << Ptr1->Location << "," << NumPlats << "," << NumTrainsAtLoc << "," << Ptr1->AtLocTime << " (" << FrhCount << " remain here)," << ServiceAndRepeatNumTotalOutput << '\n';
17478  }
17479  LastFrhCount = FrhCount;
17480  PreviousServiceAndRepeatNumTotalOutput = ServiceAndRepeatNumTotalOutput;
17481  AtLocsPrinted = true;
17482  ServiceAndRepeatNumTotal = "";
17483  }
17484  }
17485  if(Ptr2 == LocServiceTimesVector.end())
17486  {
17487  break;
17488  }
17489  if(Ptr2->Location != Ptr1->Location)
17490  {
17491  break;
17492  }
17493  }
17494  if(Ptr2 == LocServiceTimesVector.end())
17495  {
17496  break;
17497  }
17498  }
17499  }
17500  if(!AtLocsPrinted)
17501  {
17502  TTFile3 << "Nothing to report for trains at locations";
17503  }
17504  TTFile3 << "\n\n";
17505  //end of simultaneous AtLocs
17506  }
17507  SequenceLog += "9\n";
17508 /*
17509  //print out the full vector here for testing purposes
17510  TTFile3 << "Full LocServiceTimesVector\n\n";
17511  TTFile3 << "Location,AtLocTime,ArrTime,DepTime,ServiceAndRepeatNum,Description\n\n";
17512 
17513  for(TLocServiceTimesVector::iterator Ptr = LocServiceTimesVector.begin(); Ptr != LocServiceTimesVector.end(); Ptr++)
17514  {
17515  TTFile3 << Ptr->Location << "," << Ptr->AtLocTime << "," << Ptr->ArrTime << "," << Ptr->DepTime << "," << Ptr->ServiceAndRepeatNum << "," << Ptr->FrhMarker << '\n';
17516  }
17517 
17518  TTFile3 << "\n\n\n";
17519 */
17520 /*cdt analysis - added at v2.10.0
17521 2 pass system: 1st extract as a single service all Snt (or Snt-sh) starts, with Fns/Sns links combined (and F-nshs/Sns-sh) (though add a new
17522 changeover code [chr XXXX - 'change ref + new reference] until come to Fjo, Frh, Frh-sh, Fer (ignore exit loc as can't stop there), ignore jbos &
17523 repeats, but with fsp & rsp store all the foregoing service entries along with the split reference & add that to the relevant Sfs entry as a new
17524 service. For shuttles with feeder start with feeder & progress into shuttle, ending when finish & remain here or progressing into the finishing
17525 service.
17526 
17527 Use The TrainDataVectorCopy as that has all unique service refs.
17528 
17529 2nd run the cdt checker similar to that in SecondPassActions, but where a same name found either side of a changeover code quote both refs. Add a
17530 similar unexpected cdt check where if have different locs either side of a cdt then may be inappropriate.
17531 
17532 First create a new TrainDataVector from earlier copy as above with single services
17533 */
17534  if(DirChecked)
17535  {
17536  //direction analysis added at v2.10.0
17537  TTrainDataEntry SingleServiceEntry, PartServiceEntry, NewPartServiceEntry, TempEntry;
17538  TTrainDataVector SingleServiceVector, PartServiceVector;
17539 
17540  //find new train services (Snt or Snt-sh) & remember that entries can be in any order
17541  //NB: ALWAYS use OtherHeadCode (which is now a service reference) for any follow-on service
17542  TTFile3 << "Train direction analysis - consisting of train facing directions on creation and possible missing or questionable changes of direction:\n\n";
17543  for(unsigned int x = 0; x < TrainDataVectorCopy.size(); x++)
17544  {
17545  TTrainDataEntry TDE = TrainDataVectorCopy.at(x);
17546  if(TDE.ActionVector.at(TDE.ActionVector.size() - 1).FormatType == Repeat)
17547  {
17548  TDE.ActionVector.erase(&TDE.ActionVector.back()); //strip repeat entry if present
17549  }
17550  const TActionVector &AV = TDE.ActionVector;
17551  if((AV.at(0).Command == "Snt") || (AV.at(0).Command == "Snt-sh"))
17552  {
17553  SingleServiceEntry = TDE;
17554  TActionVector &SSAV = SingleServiceEntry.ActionVector;
17555  for(unsigned int y = 0; y < SSAV.size(); y++)
17556  {
17557  if((SSAV.at(y).Command == "Fjo") || (SSAV.at(y).Command == "Frh") || (SSAV.at(y).Command == "Fer") || (SSAV.at(y).Command == "Frh-sh"))
17558  {
17559  SingleServiceVector.push_back(SingleServiceEntry); //push the complete entry
17560  break; //finished with this one
17561  }
17562  else if((SSAV.at(y).Command == "fsp") || (SSAV.at(y).Command == "rsp"))
17563  {
17564  PartServiceEntry = TDE; //start with complete entry
17565  PartServiceEntry.ActionVector.clear(); //clear AV
17566  for(unsigned int z = 0; z <= y; z++)
17567  {
17568  PartServiceEntry.ActionVector.push_back(TDE.ActionVector.at(z)); //add back all AVs up to & inc fsp/rsp
17569  if(z == y)
17570  {
17571  PartServiceEntry.ActionVector.at(z).Command = "chr-sp"; //change split command to chr
17572  PartServiceEntry.ActionVector.at(z).OtherHeadCode = PartServiceEntry.ActionVector.at(z).LinkedTrainEntryPtr->ServiceReference;
17573  }
17574  }
17575  PartServiceVector.push_back(PartServiceEntry);
17576  if(SSAV.at(y).Command == "fsp")
17577  {
17578  SSAV.at(y).Command = "Front split - original service continues below";
17579  SSAV.at(y).OtherHeadCode = "";
17580  }
17581  if(SSAV.at(y).Command == "rsp")
17582  {
17583  SSAV.at(y).Command = "Rear split - original service continues below";
17584  SSAV.at(y).OtherHeadCode = "";
17585  }
17586  //don't break & continue here because the original train carries on
17587  }
17588  else if(SSAV.at(y).Command == "Fns")
17589  {
17590  SSAV.at(y).Command = "chr-Fns";
17591  SSAV.at(y).OtherHeadCode = SSAV.at(y).LinkedTrainEntryPtr->ServiceReference;
17592  PartServiceVector.push_back(SingleServiceEntry); //not complete yet
17593  break; //from y loop
17594  }
17595  else if(SSAV.at(y).Command == "Fns-sh")
17596  {
17597  SSAV.at(y).Command = "chr-Fns-sh";
17598  SSAV.at(y).OtherHeadCode = SSAV.at(y).NonRepeatingShuttleLinkEntryPtr->ServiceReference;
17599  SSAV.at(y).NonRepeatingShuttleLinkHeadCode = "";
17600  PartServiceVector.push_back(SingleServiceEntry); //not complete yet
17601  break; //from y loop
17602  }
17603  else if(SSAV.at(y).Command == "F-nshs")
17604  {
17605  SSAV.at(y).Command = "chr-F-nshs"; //NonRepeatingShuttleLinkHeadCode is the shuttle headcode
17606  SSAV.at(y).OtherHeadCode = SSAV.at(y).LinkedTrainEntryPtr->ServiceReference;
17607  SSAV.at(y).NonRepeatingShuttleLinkHeadCode = "";
17608  PartServiceVector.push_back(SingleServiceEntry); //not complete yet
17609  break; //from y loop
17610  }
17611  }
17612  }
17613  }
17614  SequenceLog += "10\n";
17615  //now have all complete entries in SingleServiceVector and all part services in PartServiceVector but without any follow-ons
17616 
17617  //Now add Sns & Sns-sh services to PartServiceVector entries
17618  AnsiString NextRef;
17619  while(!PartServiceVector.empty())
17620  {
17621  PartServiceEntry = PartServiceVector.at(0); //deal with front entry and add new entries at the back
17622  for(unsigned int y = 0; y < PartServiceEntry.ActionVector.size(); y++)
17623  {
17624  if(PartServiceEntry.ActionVector.at(y).Command.SubString(1,3) == "chr")
17625  {
17626  NextRef = PartServiceEntry.ActionVector.at(y).OtherHeadCode;
17627  }
17628  }
17629  //find it in TrainDataVectorCopy
17630  bool FinishType = true, FoundFlag = false;
17631  while(FinishType)
17632  {
17633  TempEntry = GetServiceFromVector(0, NextRef, TrainDataVectorCopy, FinishType, FoundFlag); //FinishType is a bool where false = Final (Fjo, Frh, Fer, or
17634  //Frh-sh); true = MoreToCome (Fns, Fns-sh, F-nshs)
17635  if(FoundFlag)
17636  {
17637  for(unsigned int y = 1; y < TempEntry.ActionVector.size(); y++) //starts at 1 as that is the entry after the start entry
17638  {
17639  if((TempEntry.ActionVector.at(y).Command == "") && (TempEntry.ActionVector.at(y).FormatType != Repeat))
17640  {
17641  PartServiceVector.at(0).ActionVector.push_back(TempEntry.ActionVector.at(y));
17642  }
17643  else if((TempEntry.ActionVector.at(y).Command[1] != 'F') && (TempEntry.ActionVector.at(y).Command != "fsp") && (TempEntry.ActionVector.at(y).Command != "rsp") && (TempEntry.ActionVector.at(y).FormatType != Repeat))
17644  {
17645  PartServiceVector.at(0).ActionVector.push_back(TempEntry.ActionVector.at(y));
17646  }
17647  else
17648  {
17649  if((TempEntry.ActionVector.at(y).Command == "Fjo") || (TempEntry.ActionVector.at(y).Command == "Frh") || (TempEntry.ActionVector.at(y).Command == "Fer") || (TempEntry.ActionVector.at(y).Command == "Frh-sh"))
17650  {
17651  PartServiceVector.at(0).ActionVector.push_back(TempEntry.ActionVector.at(y));
17652  SingleServiceVector.push_back(PartServiceVector.at(0)); //push the complete entry
17653  PartServiceVector.erase(&PartServiceVector.at(0));
17654  break; //from y loop
17655  }
17656  else if((TempEntry.ActionVector.at(y).Command == "fsp") || (TempEntry.ActionVector.at(y).Command == "rsp"))
17657  {
17658  NewPartServiceEntry = PartServiceVector.at(0); //covers everything up to but excluding the split
17659  NewPartServiceEntry.ActionVector.push_back(TempEntry.ActionVector.at(y)); //now includes the split
17660  NewPartServiceEntry.ActionVector.at(NewPartServiceEntry.ActionVector.size() - 1).Command = "chr-sp"; //change split command to chr
17661  NewPartServiceEntry.ActionVector.at(NewPartServiceEntry.ActionVector.size() - 1).OtherHeadCode = NewPartServiceEntry.ActionVector.at(NewPartServiceEntry.ActionVector.size() - 1).LinkedTrainEntryPtr->ServiceReference;
17662  PartServiceVector.push_back(NewPartServiceEntry); //new entry for the split service
17663  if(TempEntry.ActionVector.at(y).Command == "fsp")
17664  {
17665  TempEntry.ActionVector.at(y).Command = "Front split - original service continues below";
17666  TempEntry.ActionVector.at(y).OtherHeadCode = "";
17667  }
17668  if(TempEntry.ActionVector.at(y).Command == "rsp")
17669  {
17670  TempEntry.ActionVector.at(y).Command = "Rear split - original service continues below";
17671  TempEntry.ActionVector.at(y).OtherHeadCode = "";
17672  }
17673  PartServiceVector.at(0).ActionVector.push_back(TempEntry.ActionVector.at(y));
17674  }
17675  else if(TempEntry.ActionVector.at(y).Command == "Fns")
17676  {
17677  TempEntry.ActionVector.at(y).Command = "chr-Fns";
17678  NextRef = TempEntry.ActionVector.at(y).LinkedTrainEntryPtr->ServiceReference;
17679  TempEntry.ActionVector.at(y).OtherHeadCode = NextRef;
17680  PartServiceVector.at(0).ActionVector.push_back(TempEntry.ActionVector.at(y)); //not complete yet
17681  break; //from y loop
17682  }
17683  else if(TempEntry.ActionVector.at(y).Command == "Fns-sh")
17684  {
17685  TempEntry.ActionVector.at(y).Command = "chr-Fns-sh";
17686  TempEntry.ActionVector.at(y).OtherHeadCode = TempEntry.ActionVector.at(y).NonRepeatingShuttleLinkEntryPtr->ServiceReference;
17687  TempEntry.ActionVector.at(y).NonRepeatingShuttleLinkHeadCode = "";
17688  NextRef = TempEntry.ActionVector.at(y).NonRepeatingShuttleLinkEntryPtr->ServiceReference;
17689  PartServiceVector.at(0).ActionVector.push_back(TempEntry.ActionVector.at(y)); //not complete yet
17690  break; //from y loop
17691  }
17692  else if(TempEntry.ActionVector.at(y).Command == "F-nshs")
17693  {
17694  TempEntry.ActionVector.at(y).Command = "chr-F-nshs"; //NonRepeatingShuttleLinkHeadCode is the shuttle headcode
17695  TempEntry.ActionVector.at(y).OtherHeadCode = TempEntry.ActionVector.at(y).LinkedTrainEntryPtr->ServiceReference;
17696  TempEntry.ActionVector.at(y).NonRepeatingShuttleLinkHeadCode = "";
17697  NextRef = TempEntry.ActionVector.at(y).LinkedTrainEntryPtr->ServiceReference;
17698  PartServiceVector.at(0).ActionVector.push_back(TempEntry.ActionVector.at(y)); //not complete yet
17699  break; //from y loop
17700  }
17701  }
17702  }
17703  }
17704  else
17705  {
17706  SequenceLog += + "11\n";
17707  throw Exception("Unable to find service reference " + NextRef + " Last ref checked = " + TempEntry.ServiceReference);
17708  }
17709  }
17710  }
17711  if(!PartServiceVector.empty())
17712  {
17713  SequenceLog += "12\n";
17714  throw Exception("PartServiceVector should be empty here - size = " + PartServiceVector.size());
17715  }
17716  SequenceLog += "13\n";
17717  /*
17718  form:-
17719  HeadCode[;Description (plain text, no commas or semicolons)][;StartSpeed(kph); MaxRunningSpeed(kph); Mass(tonnes, prog converts to kg);
17720  MaxBrakeRate(tonnes force, prog converts to m/s/s); & gross power(kW, prog converts to power at rail in w)
17721  then multiple entries, separated by commas, of the form:-
17722 
17723  HH:MM;Snt;RearStartIdent FrontStartIdent }StartNew }
17724  HH:MM;Snt-sh;RearStartIdent FrontStartIdent;Fsh HeadCode }SNTShuttle }
17725  HH:MM;Sns-sh;Fxx-sh HeadCode;F-nshs HeadCode (non-repeating)}SNSShuttle }
17726 
17727  HH:MM;Command;HeadCode (Sfs Sns jbo fsp rsp Fns Fjo Frh-sh) }TimeCmdHeadCode } Train action entries
17728  HH:MM;F-nshs;NonRepeatingShuttleLinkHeadCode }FNSNonRepeatToShuttle }
17729  HH:MM;Sns-fsh;NonRepeatingShuttleLinkHeadCode }SNSNonRepeatFromShuttle }
17730 
17731  HH:MM;Command (cdt) }TimeCmd }
17732  HH:MM;Location (arr & dep) }TimeLoc }
17733  HH:MM;HH:MM;Location }TimeTimeLoc }
17734  HH:MM;pas;Location }PassTime }
17735  HH:MM;Fns-sh;Snx-sh HeadCode;Sns-fsh HeadCode (non-rep) }FSHNewService }
17736  HH:MM;Fer;set of allowable IDs }ExitRailway }
17737  Command (Frh only) }FinRemHere }
17738 
17739  R;mm;dd;nn. Repeat Repeat entry
17740 
17741  Formats:
17742 
17743  Command only: Frh
17744  Time;Command: cdt
17745  Time;Command;Headcode: Sfs Sns jbo fsp rsp Fns Fjo Frh-sh F-nshs Sns-fsh
17746  Time;Command;2 Element IDs: Snt
17747  Time;Comand;n Element IDs: Fer
17748  Time;Command;rep Headcode;nonrep Headcode: Sns-sh Fns-sh
17749  Time;Command;2 Element IDs;Headcode Snt-sh
17750  Time;Command;Location pas
17751  Time;Location Arr Dep
17752  Time;Time;Location Arr & dep together
17753  */
17754 
17755 /*
17756 Perform the starting direction check (Snt & Snt-sh entries). Starts with the train's front element,
17757 checking forwards until it comes to a continuation (no report), a location name that is not null and
17758 different to the train's front element name (whether null or not) (no report), a leading point
17759 (no report) or buffers (report).
17760 */
17761  bool BufferFacingUnReportedFlag = true;
17762  for(unsigned int x = 0; x < SingleServiceVector.size(); x++)
17763  {
17764  TTrackElement ThisElement, NextElement;
17765  TTrainDataEntry TDE = SingleServiceVector.at(x);
17766  if(TDE.ActionVector.at(TDE.ActionVector.size() - 1).FormatType == Repeat)
17767  {
17768  SequenceLog += "13a\n";
17769  throw Exception("Repeat entry present in SingleServiceVector at position " + x);
17770  }
17771  const TActionVector &AV = TDE.ActionVector;
17772  if((AV.at(0).Command == "Snt") || (AV.at(0).Command == "Snt-sh"))
17773  {
17774  bool BufferFlag = false;
17775  int FrontTVPos = AV.at(0).FrontStartOrRepeatDigits;
17776  int RearTVPos = AV.at(0).RearStartOrRepeatMins;
17777  AnsiString FrontLocName = AV.at(0).LocationName;
17778  int NextEntryPos, NextExitPos;
17779  ThisElement = Track->TrackElementAt(1395, FrontTVPos);
17780  int ThisExitPos;
17781  if(ThisElement.Conn[0] == RearTVPos)
17782  {
17783  ThisExitPos = 1;
17784  }
17785  else if(ThisElement.Conn[1] == RearTVPos)
17786  {
17787  ThisExitPos = 0;
17788  }
17789  else if(ThisElement.Conn[2] == RearTVPos)
17790  {
17791  ThisExitPos = 3;
17792  }
17793  else if(ThisElement.Conn[3] == RearTVPos)
17794  {
17795  ThisExitPos = 2;
17796  }
17797  if((ThisElement.TrackType == Buffers) && (ThisExitPos == 0))//pos 0 is the buffer
17798  {
17799  BufferFlag = true;
17800  }
17801  else //continue tracking forwards
17802  {
17803  while(true)
17804  {
17805  if(ThisElement.Conn[ThisExitPos] == -1)
17806  {
17807  SequenceLog = "13b\n";
17808  throw Exception("ThisElement connects to -1 for " + TDE.ServiceReference);
17809  }
17810  NextElement = Track->TrackElementAt(1396, ThisElement.Conn[ThisExitPos]);
17811  NextEntryPos = ThisElement.ConnLinkPos[ThisExitPos];
17812  if((NextElement.TrackType == Points) && ((NextEntryPos == 0) || (NextEntryPos == 2))) //leading points
17813  {
17814  BufferFlag = false; //should already be false
17815  break;
17816  }
17817  else if(NextElement.TrackType == Continuation)
17818  {
17819  BufferFlag = false;
17820  break;
17821  }
17822  else if((NextElement.ActiveTrackElementName != "") && (NextElement.ActiveTrackElementName != FrontLocName))
17823  {
17824  BufferFlag = false;
17825  break;
17826  }
17827  else if(NextElement.TrackType == Buffers)
17828  {
17829  BufferFlag = true;
17830  break;
17831  }
17832  else if((NextElement.TrackType == Points) && ((NextEntryPos == 1) || (NextEntryPos == 3))) //trailing points
17833  {
17834  ThisElement = NextElement;
17835  ThisExitPos = 0;
17836  continue;
17837  }
17838  else
17839  {
17840  if(NextEntryPos == 0)
17841  {
17842  NextExitPos = 1;
17843  }
17844  else if(NextEntryPos == 1)
17845  {
17846  NextExitPos = 0;
17847  }
17848  else if(NextEntryPos == 2)
17849  {
17850  NextExitPos = 3;
17851  }
17852  else if(NextEntryPos == 3)
17853  {
17854  NextExitPos = 2;
17855  }
17856  }
17857  ThisElement = NextElement;
17858  ThisExitPos = NextExitPos;
17859  }
17860  }
17861  if(BufferFlag)
17862  {
17863  if(BufferFacingUnReportedFlag)
17864  {
17865  TTFile3 << "Train facing direction on creation analysis:-\n\n";
17866  BufferFacingUnReportedFlag = false;
17867  }
17868  TTFile3 << "Service " + TDE.ServiceReference + " facing buffers on creation\n";
17869  }
17870  }
17871  }
17872  if(BufferFacingUnReportedFlag)
17873  {
17874  TTFile3 << "Nothing to report for train facing directions\n\n";
17875  }
17876  else
17877  {
17878  TTFile3 << '\n';
17879  }
17880  SequenceLog += "13c\n";
17881 
17882  //Perform the missing cdt check. Check every entry simiar to the check in SecondPassActions and if find any print out the full sequence and service entries
17883  AnsiString LocationNameToBeChecked = "";
17884  bool MissingcdtUnreportedFlag = true;
17885  TNumList MarkerList;
17886  for(unsigned int x = 0; x < SingleServiceVector.size(); x++)
17887  {
17888  const TTrainDataEntry &TDEntry = SingleServiceVector.at(x);
17889  unsigned int y = 0;
17890  int FirstInstance = 9999, SecondInstance = 9999; //9999 ensures wont be marked if not changed
17891  bool FullBreak = false;
17892  MarkerList.clear();
17893  const TActionVectorEntry &AVEntry0 = TDEntry.ActionVector.at(0);
17894  // first discard unlocated Snt entries as they don't have location name set
17895  if((AVEntry0.Command == "Snt") && (AVEntry0.LocationType == EnRoute))
17896  {
17897  y = 1;
17898  }
17899  while((y < TDEntry.ActionVector.size()) && !FullBreak)
17900  // need to check each location name separately in turn, skipped for SignallerControl entries
17901  {
17902  if((TDEntry.ActionVector.at(y).Command == "Fer") || (TDEntry.ActionVector.at(y).FormatType == Repeat) ||
17903  (TDEntry.ActionVector.at(y).Command == "Fjo") || (TDEntry.ActionVector.at(y).Command == "Frh") ||
17904  (TDEntry.ActionVector.at(y).Command == "Frh-sh"))
17905  {
17906  break; // out of the 'while' loop since have reached the end
17907  }
17908  LocationNameToBeChecked = TDEntry.ActionVector.at(y).LocationName;
17909  FirstInstance = y;
17910  for(unsigned int z = y; z < TDEntry.ActionVector.size(); z++)
17911  {
17912  const TActionVectorEntry &AVEntry = TDEntry.ActionVector.at(z);
17913  if((AVEntry.Command == "Fer") || (AVEntry.FormatType == Repeat) ||
17914  (AVEntry.Command == "Fjo") || (AVEntry.Command == "Frh") ||
17915  (AVEntry.Command == "Frh-sh"))
17916  {
17917  break; // out of the 'z' loop since have reached the end & 'Fer' & 'Repeat' have no location name set
17918  }
17919  if(AVEntry.Command == "cdt")
17920  {
17921  break; // out of the 'z' loop since the check is only valid up to a change of direction
17922  }
17923  if(AVEntry.LocationName == LocationNameToBeChecked)
17924  {
17925  continue; // keep going while name same
17926  }
17927  if(AVEntry.LocationName != LocationNameToBeChecked)
17928  // if name different check forwards to see if repeats
17929  {
17930  for(unsigned int a = z; a < TDEntry.ActionVector.size(); a++)
17931  {
17932  if(TDEntry.ActionVector.at(a).Command == "cdt")
17933  {
17934  break; // out of the 'a' & 'z' loops since the check is only valid up to a change of direction
17935  }
17936  if(TDEntry.ActionVector.at(a).LocationName == LocationNameToBeChecked)
17937  {
17938  SecondInstance = a;
17939  AnsiString Sequence = TDEntry.ServiceReference;
17940  for(unsigned int b = 0; b < TDEntry.ActionVector.size(); b++)
17941  {
17942  if(TDEntry.ActionVector.at(b).Command.SubString(1,3) == "chr")
17943  {
17944  Sequence = Sequence + AnsiString(" -> ") + TDEntry.ActionVector.at(b).OtherHeadCode;
17945  }
17946  }
17947  if(MissingcdtUnreportedFlag)
17948  {
17949  TTFile3 << "Possibly missing changes of direction - these will be missing unless the service travels in a loop back to the locations marked:-\n\n";
17950  }
17951  TTFile3 << LocationNameToBeChecked << " is listed twice with no direction change between in service sequence: " << Sequence << "\n\n";
17952  MarkerList.push_back(FirstInstance);
17953  MarkerList.push_back(SecondInstance);
17954  SingleServiceOutput(0, x, MarkerList, SingleServiceVector, TTFile3);
17955  MissingcdtUnreportedFlag = false;
17956  FullBreak = true; //no more checks for this sequence
17957  break; //out of the a & z loops
17958  }
17959  }
17960  break; // out of the 'z' loop since have checked 'a' as far as need to
17961  }
17962  }
17963  y++;
17964  }
17965  }
17966  if(MissingcdtUnreportedFlag)
17967  {
17968  TTFile3 << "Nothing to report for missing changes of direction\n\n";
17969  }
17970  else
17971  {
17972  TTFile3 << '\n';
17973  }
17974  SequenceLog += "14\n";
17975 
17976 /* Perform the questionable cdt check. Examine each service in the SingleServiceVector, and if don't find the same
17977  name either side of a cdt (before the next cdt) then flag as a questionable cdt.
17978  Method: Have an outer loop for each service that looks for cdts. When found work backwards to the last cdt or beginning and std::list all the
17979  locations excluding the cdt location. Then work forwards to the next cdt or the end and do the same. Sort each list and make unique (duplicated
17980  names on one side of a cdt already checked either by the tt validator or the missing cdt check. Then compare the two lists and if any location
17981  included in both then ok, else report as questionable. If one list is empty then it is reported.
17982 */
17983  typedef std::list<AnsiString> TLocList;
17984  TLocList BackwardList, ForwardList;
17985  bool IntroLineNeeded = true;
17986  for(unsigned int x = 0; x < SingleServiceVector.size(); x++)
17987  {
17988  unsigned int cdtPosition = 9999;
17989  AnsiString cdtLocation = "";
17990  bool FoundSameName = false;
17991  MarkerList.clear();
17992  const TTrainDataEntry &TDEntry = SingleServiceVector.at(x);
17993  for(unsigned int y = 0; y < TDEntry.ActionVector.size(); y++)
17994  // need to check each location name separately in turn, skipped for SignallerControl entries
17995  {
17996  BackwardList.clear();
17997  ForwardList.clear();
17998  const TActionVectorEntry &AVEntry = TDEntry.ActionVector.at(y);
17999  if((AVEntry.Command == "Fer") || (AVEntry.FormatType == Repeat) || //shouldn't be any repeats
18000  (AVEntry.Command == "Fjo") || (AVEntry.Command == "Frh") ||
18001  (AVEntry.Command == "Frh-sh"))
18002  {
18003  if(MarkerList.empty())
18004  {
18005  break; // out of the 'y' loop since have reached the end & nothing to report
18006  }
18007  else
18008  {
18009  AnsiString Sequence = TDEntry.ServiceReference;
18010  for(unsigned int b = 0; b < TDEntry.ActionVector.size(); b++)
18011  {
18012  if(TDEntry.ActionVector.at(b).Command.SubString(1,3) == "chr")
18013  {
18014  Sequence = Sequence + AnsiString(" -> ") + TDEntry.ActionVector.at(b).OtherHeadCode;
18015  }
18016  }
18017  MarkerList.sort();
18018  if(IntroLineNeeded)
18019  {
18020  TTFile3 << "Questionable change of direction analysis.\n\n";
18021  TTFile3 << "For marked changes of direction there are no same-name locations listed both above and below.\n";
18022  TTFile3 << "These changes of direction are probably valid for movements to and from depots but all should be checked to\n";
18023  TTFile3 << "make sure that none has been included incorrectly:\n\n";
18024  IntroLineNeeded = false;
18025  }
18026  TTFile3 << "Service sequence " << Sequence << " contains questionable changes of direction:-\n\n";
18027  SingleServiceOutput(1, x, MarkerList, SingleServiceVector, TTFile3);
18028  break;
18029  }
18030  }
18031  if(AVEntry.Command != "cdt")
18032  {
18033  continue; //only looking for cdts
18034  }
18035  //here have found a cdt
18036  cdtPosition = y;
18037  cdtLocation = AVEntry.LocationName;
18038  for(int z = y - 1; z >= 0; z--)
18039  {
18040  const TActionVectorEntry &AVEntry2 = TDEntry.ActionVector.at(z);
18041  if(AVEntry2.Command == "cdt")
18042  {
18043  break; //don't look further back than the last cdt
18044  }
18045  if((AVEntry2.LocationName != "") && (AVEntry2.LocationName != cdtLocation)) //if an earlier entry == cdtLocation will have been picked up in an earlier check
18046  {
18047  BackwardList.push_back(AVEntry2.LocationName);
18048  }
18049  }
18050  BackwardList.sort();
18051  BackwardList.unique();
18052  for(unsigned int z = y + 1; z < TDEntry.ActionVector.size(); z++)
18053  {
18054  const TActionVectorEntry &AVEntry3 = TDEntry.ActionVector.at(z);
18055  if((AVEntry3.Command == "Fer") || (AVEntry3.FormatType == Repeat) ||
18056  (AVEntry3.Command == "Fjo") || (AVEntry3.Command == "Frh") ||
18057  (AVEntry3.Command == "Frh-sh") || (AVEntry3.Command == "cdt"))
18058  {
18059  break; // out of the 'z' loop since have reached another cdt or the end
18060  }
18061  if((AVEntry3.LocationName != "") && (AVEntry3.LocationName != cdtLocation)) //if a later entry == cdtLocation will have been picked up in an earlier check
18062  {
18063  ForwardList.push_back(AVEntry3.LocationName);
18064  }
18065  }
18066  ForwardList.sort();
18067  ForwardList.unique();
18068  FoundSameName = false;
18069  //now have both lists compiled (may be empty) so check for same name in both & report if don't find any
18070  if(!BackwardList.empty() && !ForwardList.empty())
18071  {
18072  for(TLocList::iterator BLIt = BackwardList.begin(); BLIt != BackwardList.end(); BLIt++)
18073  {
18074  for(TLocList::iterator FLIt = ForwardList.begin(); FLIt != ForwardList.end(); FLIt++)
18075  {
18076  if(*BLIt == *FLIt)
18077  {
18078  FoundSameName = true;
18079  }
18080  }
18081  }
18082  }
18083  if(!FoundSameName) //report the inability to find same name
18084  {
18085  MarkerList.push_back(cdtPosition);
18086  }
18087  }
18088  }
18089  if(IntroLineNeeded)
18090  {
18091  TTFile3 << "Nothing to report for questionable changes of direction\n\n";
18092  }
18093  else
18094  {
18095  TTFile3 << '\n';
18096  }
18097 /*
18098 //print all SSVector for diagnostic purposes
18099  TTFile3 << "Whole SSVector\n\n";
18100  TNumList EmptyList;
18101  for(unsigned int x = 0; x < SingleServiceVector.size(); x++)
18102  {
18103  SingleServiceOutput(, x, EmptyList, SingleServiceVector, TTFile3);
18104  }
18105 */
18106  }
18107  SequenceLog += "15\n";
18108  TTFile3.close();
18109  Utilities->CallLogPop(2212);
18110  return(true);
18111  }
18112 
18113  catch(const Exception &e) //non error catch
18114  {
18115  AnsiString TTErrorFileName = "Analysis Error.txt";
18116  TTErrorFileName = CurDir + "\\Formatted timetables\\" + TTErrorFileName;
18117  std::ofstream TTError(TTErrorFileName.c_str());
18118  if(TTError == 0)
18119  {
18120  ShowMessage("Analysis error file failed to open - can't be created");
18121  Utilities->CallLogPop(2233);
18122  return(false);
18123  }
18124  AnsiString TimeNow = TDateTime::CurrentDateTime().FormatString("dd-mm-yyyy hh.nn.ss");
18125  TTError << TimeNow.c_str() << '\n' << ArrRange << '\n' << ArrChecked << '\n' << DepRange << '\n' <<
18126  DepChecked << '\n' << AtLocChecked << '\n' << SequenceLog << '\n' << AnsiString(e.Message);
18127 
18128  TTError.close();
18129  ShowMessage("Error in Conflict Analysis: A file called 'Analysis Error.txt' has been created in your Formatted timetables folder. Please send this file together with your railway and timetable files to railwayfeedback@gmail.com for investigation - many thanks");
18130  Utilities->CallLogPop(2226);
18131  return(false);
18132  }
18133 }
18134 
18135 // ---------------------------------------------------------------------------
18136 void TTrainController::SingleServiceOutput(int Caller, int SSVectorNumber, TNumList MarkerList, TTrainDataVector &SingleServiceVector, std::ofstream &VecFile)
18137 {
18138  Utilities->CallLog.push_back(Utilities->TimeStamp() + AnsiString(SSVectorNumber) + ',' + ",SingleServiceOutput");
18139  if((SSVectorNumber < 0) || ((unsigned int)SSVectorNumber >= SingleServiceVector.size()))
18140  {
18141  throw Exception("SSVectorNumber out of range, = " + AnsiString(SSVectorNumber) + ", Vector size = " + SingleServiceVector.size());
18142  }
18143  TTrainDataEntry SingleService = SingleServiceVector.at(SSVectorNumber);
18144  {
18145  VecFile << ",Initial service reference " << SingleService.ServiceReference + '\n';
18146  AnsiString Marker = "";
18147  for(unsigned int x = 0; x < SingleService.ActionVector.size(); x++)
18148  {
18149  Marker = ',';
18150  for(TNumListIterator MLIt = MarkerList.begin(); MLIt != MarkerList.end(); MLIt++)
18151  {
18152  if(int(x) == *MLIt)
18153  {
18154  Marker = "-->,";
18155  break;
18156  }
18157  }
18158  TActionVectorEntry AVE = SingleService.ActionVector.at(x);
18159  if(AVE.FormatType == StartNew)
18160  {
18161  AnsiString RearID = Track->TrackElementAt(1397, AVE.RearStartOrRepeatMins).ElementID;
18162  AnsiString FrontID = Track->TrackElementAt(1398, AVE.FrontStartOrRepeatDigits).ElementID;
18163  VecFile << Marker << AnsiString(AVE.EventTime.TimeString()) << ' ' << AVE.Command << ' ' << RearID << ' ' << FrontID << '\n';
18164  }
18165  if(AVE.FormatType == SNTShuttle)
18166  {
18167  AnsiString RearID = Track->TrackElementAt(1399, AVE.RearStartOrRepeatMins).ElementID;
18168  AnsiString FrontID = Track->TrackElementAt(1400, AVE.FrontStartOrRepeatDigits).ElementID;
18169  VecFile << Marker << AnsiString(AVE.EventTime.TimeString()) << ' ' << AVE.Command << ' ' << RearID << ' ' << FrontID << ' ' << AVE.OtherHeadCode << '\n';
18170  }
18171  if(AVE.FormatType == SNSShuttle) //should all have been converted to chr
18172  {
18173  VecFile << Marker << AnsiString(AVE.EventTime.TimeString()) << ' ' << AVE.Command << ' ' << AVE.OtherHeadCode << ' ' << AVE.NonRepeatingShuttleLinkHeadCode << '\n';
18174  }
18175  if(AVE.FormatType == Repeat) //shouldn't be any repeats, only here to show if any have been copied
18176  {
18177  VecFile << Marker << "Repeat " << AVE.RearStartOrRepeatMins << ' ' << AVE.FrontStartOrRepeatDigits << ' ' << AVE.NumberOfRepeats << '\n';
18178  }
18179  if((AVE.FormatType == TimeCmd) || (AVE.FormatType == TimeCmdHeadCode) || (AVE.FormatType == FNSNonRepeatToShuttle) || (AVE.FormatType == FSHNewService))
18180  {
18181  TActionVectorEntry AVHolder = AVE;
18182  if(AVE.Command.SubString(1,3) == "chr")
18183  {
18184  if(AVE.Command.SubString(5, AVE.Command.Length() - 4) == "sp")
18185  {
18186  AVE.Command = "Change of service to " + AVE.OtherHeadCode + " after split";
18187  AVE.OtherHeadCode = "";
18188  }
18189  if(AVE.Command.SubString(5, AVE.Command.Length() - 4) == "Fns")
18190  {
18191  AVE.Command = "Change of service to ";
18192  }
18193  if(AVE.Command.SubString(5, AVE.Command.Length() - 4) == "Fns-sh")
18194  {
18195  AVE.Command = "Change to shuttle finishing service";
18196  }
18197  if(AVE.Command.SubString(5, AVE.Command.Length() - 4) == "F-nshs")
18198  {
18199  AVE.Command = "Change to shuttle service " + AVE.OtherHeadCode + " from feeder";
18200  AVE.OtherHeadCode = "";
18201  }
18202  }
18203  VecFile << Marker << AnsiString(AVE.EventTime.TimeString()) << ' ' << AVE.Command << ' ' << AVE.OtherHeadCode << '\n';
18204  AVE = AVHolder;
18205  }
18206  else if((AVE.FormatType == TimeLoc) && (AVE.ArrivalTime != TDateTime(-1)))
18207  {
18208  VecFile << Marker << AnsiString(AVE.ArrivalTime.TimeString()) << " Arr " << AVE.LocationName << '\n';
18209  }
18210  else if((AVE.FormatType == TimeLoc) && (AVE.DepartureTime != TDateTime(-1)))
18211  {
18212  VecFile << Marker << AnsiString(AVE.DepartureTime.TimeString()) << " Dep " << AVE.LocationName << '\n';
18213  }
18214  else if(AVE.FormatType == TimeTimeLoc)
18215  {
18216  VecFile << Marker << AnsiString(AVE.ArrivalTime.TimeString()) << ' ' << AnsiString(AVE.DepartureTime.TimeString()) << ' ' << AVE.LocationName << '\n';
18217  }
18218  else if(AVE.FormatType == PassTime)
18219  {
18220  VecFile << Marker << AnsiString(AVE.EventTime.TimeString()) << ' ' << "Pass" << ' ' << AVE.LocationName << '\n';
18221  }
18222  else if(AVE.FormatType == ExitRailway) //ListOfExits added at v2.10.0
18223  {
18224  AnsiString ListOfExits = "";
18225  for(TNumListIterator NLIt = AVE.ExitList.begin(); NLIt != AVE.ExitList.end(); NLIt++)
18226  {
18227  ListOfExits += AnsiString(Track->TrackElementAt(1432, *NLIt).ElementID) + ' ';
18228  }
18229  VecFile << Marker << AnsiString(AVE.EventTime.TimeString()) << " Fer " << ListOfExits <<'\n';
18230  }
18231  else if(AVE.FormatType == FinRemHere)
18232  {
18233  VecFile << Marker << "Frh" << '\n';
18234  }
18235  }
18236  VecFile << '\n';
18237  }
18238  Utilities->CallLogPop(2318);
18239 }
18240 
18241 // ---------------------------------------------------------------------------
18242 
18243 TTrainDataEntry TTrainController::GetServiceFromVector(AnsiString Caller, AnsiString ServiceReference, TTrainDataVector Vector, bool &FinishType, bool &FoundFlag)
18244 {
18245  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetServiceFromVector," + ServiceReference);
18246  FoundFlag = false;
18247  FinishType = true;
18248  for(unsigned int x = 0; x < Vector.size(); x++)
18249  {
18250  if(Vector.at(x).ServiceReference == ServiceReference)
18251  {
18252  if(Vector.at(x).ActionVector.at(Vector.at(x).ActionVector.size() - 1).FormatType == Repeat) //shouldn't be any repeats
18253  {
18254  TActionVectorEntry AVE = Vector.at(x).ActionVector.at(Vector.at(x).ActionVector.size() - 2);
18255  if((AVE.Command == "Fjo") || (AVE.Command == "Frh") || (AVE.Command == "Fer") || (AVE.Command == "Frh-sh"))
18256  {
18257  FinishType = false;
18258  }
18259  }
18260  else
18261  {
18262  TActionVectorEntry AVE = Vector.at(x).ActionVector.at(Vector.at(x).ActionVector.size() - 1);
18263  if((AVE.Command == "Fjo") || (AVE.Command == "Frh") || (AVE.Command == "Fer") || (AVE.Command == "Frh-sh"))
18264  {
18265  FinishType = false;
18266  }
18267  }
18268  FoundFlag = true;
18269  Utilities->CallLogPop(2319);
18270  return(Vector.at(x));
18271  }
18272  }
18273  Utilities->CallLogPop(2320);
18274  return(Vector.at(Vector.size() - 1)); //return last for want of returning something
18275 }
18276 
18277 // ---------------------------------------------------------------------------
18278 
18279 bool TTrainController::WithinTimeRange(int Caller, AnsiString Time1, AnsiString Time2, int MinuteRange) //times are "HH:MM"
18280 {
18281 //convert times to integer minutes
18282  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",WithinTimeRange," + Time1 + "," + Time2 + "," + AnsiString(MinuteRange));
18283  if((Time1 == "") || (Time2 == ""))
18284  {
18285  Utilities->CallLogPop(2213);
18286  return(false);
18287  }
18288  int Mins = Time1.SubString(4,2).ToInt();
18289  int Hours = Time1.SubString(1,2).ToInt();
18290  int Time1Mins = (Hours * 60) + Mins;
18291  Mins = Time2.SubString(4,2).ToInt();
18292  Hours = Time2.SubString(1,2).ToInt();
18293  int Time2Mins = (Hours * 60) + Mins;
18294  if(abs(Time1Mins - Time2Mins) <= MinuteRange)
18295  {
18296  Utilities->CallLogPop(2214);
18297  return(true);
18298  }
18299  Utilities->CallLogPop(2215);
18300  return(false);
18301 }
18302 
18303 // ---------------------------------------------------------------------------
18304 
18305 AnsiString TTrainController::ConsolidateSARNTArrDep(int Caller, const AnsiString Input, int &NumTrainsAtLoc, AnsiString Location, bool Arrival,
18306  bool &AnalysisError, int &MaxNumberOfSameDirections)
18307 {
18308  //input consists of services and service Arr or Dep times as a comma separated list, Location needed to determine direction information
18309 
18310  try
18311  {
18312  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ConsolidateSARNTArrDep," + Input);
18313  AnsiString Output = "", OneService = "", TempStr1 = "", TempStr2 = "";
18314  int SCPos = 0;
18315  std::list<AnsiString> ServiceList; //this is the list of services with times extracted from Input - not to be confused with ServiceCallingPointsList
18316  //first change every second comma in Input to a semicolon so can separate services but keep times with services
18317  bool EvenComma = false;
18318  for(int x = 1; x <= Input.Length(); x++)
18319  {
18320  TempStr1 = Input[x];
18321  if(TempStr1 == AnsiString(',') && EvenComma)
18322  {
18323  TempStr2 += ';';
18324  }
18325  else
18326  {
18327  TempStr2 += Input[x];
18328  }
18329  if(TempStr1 == AnsiString(','))
18330  {
18331  EvenComma = !EvenComma;
18332  }
18333  }
18334  //load up the list of services with associated times
18335  while(TempStr2.Length() > 0)
18336  {
18337  SCPos = TempStr2.Pos(';');
18338  if(SCPos > 0) //0 if not found, as won't be when only one service left
18339  {
18340  OneService = TempStr2.SubString(1, SCPos - 1);
18341  ServiceList.push_back(OneService);
18342  TempStr2 = TempStr2.SubString(SCPos + 1, TempStr2.Length() - SCPos);
18343  }
18344  else //no semicolon so looking at last (or only) element
18345  {
18346  ServiceList.push_back(TempStr2);
18347  TempStr2 = "";
18348  }
18349  }
18350  ServiceList.sort(); // alphabetical order
18351  ServiceList.unique(); //remove duplicates
18352  NumTrainsAtLoc = ServiceList.size(); //calc this after removing duplicates as may not have changed
18353 
18354  //now add direction information from AllServiceCallingLocsMap - key is service ref and value a list of calling points in order
18355  int DirectionMarker = 0; //this is added in & runs from 1 upwards, same marker for diff services = same direction
18356  //first add the direction marker "&0" for not yet allocated - '&' is an identifier
18357  std::list<AnsiString>::iterator SLIt, SLIt1, SLIt2, SLIt3;
18358 
18359  for(SLIt = ServiceList.begin(); SLIt != ServiceList.end(); SLIt++)
18360  {
18361  *SLIt = *SLIt + "&0"; //add in a basic direction marker to each service
18362  }
18363  SLIt3 = ServiceList.end();
18364  SLIt3--; //so points to last element
18365  AnsiString ServiceRef1, ServiceRef2, AnsiTime1, AnsiTime2, RepeatInfo1, RepeatInfo2; //1 refers to first for..next loop & 2 to second
18366  int AmpersandPos, SpacePos, CommaPos1, CommaPos2, RepeatNum1, RepeatNum2;
18367  TAllServiceCallingLocsMap::iterator ASCLIt1, ASCLIt2;
18368  TServiceCallingLocsList ServiceCallingLocsList1, ServiceCallingLocsList2;
18369  MaxNumberOfSameDirections = 0; //at end of each SLIt loop if SameDirectionCount > MaxNumberOfSameDirections then MaxNumberOfSameDirections = SameDirectionCount
18370  int SameDirectionCount = 0; //starts at 1 at each SLIt loop (because SLIt1 entry already has a DirectionMarker) and increments for every same direction
18371 
18372  for(std::list<AnsiString>::iterator SLIt1 = ServiceList.begin(); SLIt1 != SLIt3; SLIt1++) //should be end() - 1 but can't use -1 with lists so have to improvise
18373  {
18374  SLIt = SLIt1;
18375  SLIt++; //so points to one after SLIt1
18376  if(SLIt1->SubString(SLIt1->Length() - 1, 2) != AnsiString("&0"))
18377  {
18378  continue; //already allocated so skip to the next
18379  }
18380  else
18381  {
18382  CommaPos1 = SLIt1->Pos(','); //can't be 0
18383  ServiceRef1 = SLIt1->SubString(1, CommaPos1 - 1);
18384  //but this contains "(First service..." etc so need to strip these, but use to extract RepeatNum
18385  SpacePos = ServiceRef1.Pos(' ');
18386  RepeatNum1 = 0;
18387  if(SpacePos > 0) //otherwise it's already correct
18388  {
18389  RepeatInfo1 = ServiceRef1.SubString(SpacePos + 2, ServiceRef1.Length() - SpacePos - 2); //drops the brackets and leaves "First service", "Repeat 2" etc
18390  ServiceRef1 = ServiceRef1.SubString(1, SpacePos - 1);
18391  if(RepeatInfo1[1] == 'F')
18392  {
18393  RepeatNum1 = 0;
18394  }
18395  else
18396  {
18397  SpacePos = RepeatInfo1.Pos(' ');
18398  RepeatNum1 = RepeatInfo1.SubString(SpacePos + 1, RepeatInfo1.Length() - SpacePos).ToInt();
18399  }
18400  }
18401  AnsiTime1 = SLIt1->SubString(CommaPos1 + 1, SLIt1->Length() - CommaPos1);
18402  //but this includes the "&0" etc so need to strip these
18403  AmpersandPos = AnsiTime1.Pos('&');
18404  AnsiTime1 = AnsiTime1.SubString(1, AmpersandPos - 1);
18405 
18406  ASCLIt1 = AllServiceCallingLocsMap.find(ServiceRef1);
18407  if(ASCLIt1 == AllServiceCallingLocsMap.end()) //can't find it
18408  {
18409  throw Exception("ASCLIt1 Error in " + Input);
18410  }
18411  ServiceCallingLocsList1 = ASCLIt1->second;
18412  AmpersandPos = SLIt1->Pos('&');
18413  *SLIt1 = SLIt1->SubString(1, AmpersandPos); //truncate up to & leave in the '&'
18414  *SLIt1 = *SLIt1 + AnsiString(++DirectionMarker); //now add the next marker (pre-increment), allow for it being more than one digit
18415 
18416  SameDirectionCount = 1;
18417  for(SLIt2 = SLIt; SLIt2 != ServiceList.end(); SLIt2++)
18418  {
18419  CommaPos2 = SLIt2->Pos(','); //can't be 0
18420  ServiceRef2 = SLIt2->SubString(1, CommaPos2 - 1);
18421  //but this contains "(First service..." etc so need to strip these
18422  SpacePos = ServiceRef2.Pos(' ');
18423  RepeatNum2 = 0;
18424  if(SpacePos > 0) //otherwise it's already correct
18425  {
18426  RepeatInfo2 = ServiceRef2.SubString(SpacePos + 2, ServiceRef2.Length() - SpacePos - 2); //drops the brackets and leaves "First service", "Repeat 2" etc
18427  ServiceRef2 = ServiceRef2.SubString(1, SpacePos - 1);
18428  if(RepeatInfo2[1] == 'F')
18429  {
18430  RepeatNum2 = 0;
18431  }
18432  else
18433  {
18434  SpacePos = RepeatInfo2.Pos(' ');
18435  RepeatNum2 = RepeatInfo2.SubString(SpacePos + 1, RepeatInfo2.Length() - SpacePos).ToInt();
18436  }
18437  }
18438  AnsiTime2 = SLIt2->SubString(CommaPos2 + 1, SLIt2->Length() - CommaPos2);
18439  //but this includes the "&0" etc so need to strip these
18440  AmpersandPos = AnsiTime2.Pos('&');
18441  AnsiTime2 = AnsiTime2.SubString(1, AmpersandPos - 1);
18442 
18443  ASCLIt2 = AllServiceCallingLocsMap.find(ServiceRef2);
18444  if(ASCLIt2 == AllServiceCallingLocsMap.end()) //can't find it
18445  {
18446  throw Exception("ASCLIt2 Error in " + Input);
18447  }
18448  ServiceCallingLocsList2 = ASCLIt2->second;
18449  //now compare the two
18450  if(SameDirection(0, ServiceRef1, ServiceRef2, AnsiTime1, AnsiTime2, RepeatNum1, RepeatNum2, ServiceCallingLocsList1, ServiceCallingLocsList2, Location, Arrival))
18451  {
18452  int AmpersandPos = SLIt2->Pos('&');
18453  *SLIt2 = SLIt2->SubString(1, AmpersandPos); //truncate up to & leave in the '&'
18454  *SLIt2 = *SLIt2 + AnsiString(DirectionMarker); //now add the same marker as *SLIt1
18455  SameDirectionCount++;
18456  }
18457  }
18458  if(SameDirectionCount > MaxNumberOfSameDirections)
18459  {
18460  MaxNumberOfSameDirections = SameDirectionCount;
18461  }
18462  }
18463  }
18464 
18465  if(SLIt3->SubString(SLIt3->Length() - 1, 2) == AnsiString("&0")) //*SLTIt3 is the last in the list and may not have been allocated, if not it doesn't match
18466  {
18467  //any existing direction so allocate it now
18468  AmpersandPos = SLIt3->Pos('&');
18469  *SLIt3 = SLIt3->SubString(1, AmpersandPos); //truncate up to & leave in the '&'
18470  *SLIt3 = *SLIt3 + AnsiString(++DirectionMarker);
18471  }
18472  //now change direction markers to upper case letters beginning with 'A' (and continuing with 'AA' if exceed 26) & add a comma before so have ServiceRef, DirectionMarker, Time
18473  for(SLIt = ServiceList.begin(); SLIt != ServiceList.end(); SLIt++)
18474  {
18475  //extract the DirectionMarker as an integer
18476  AmpersandPos = SLIt->Pos('&');
18477  AnsiString DirectionMarkerString = SLIt->SubString(AmpersandPos + 1, SLIt->Length() - AmpersandPos); //extract the number as an ansistring
18478  AnsiString ServiceWithoutMarker = SLIt->SubString(1, AmpersandPos - 1); //truncate the &number
18479  DirectionMarker = DirectionMarkerString.ToInt();
18480  AnsiString DirectionSuffix = "";
18481  char c;
18482  if(DirectionMarker < 27)
18483  {
18484  c = 64 + DirectionMarker; //so 1 -> 'A'
18485  DirectionSuffix = "," + AnsiString(c);
18486  }
18487  else if(DirectionMarker < 53)
18488  {
18489  c = 65 + DirectionMarker - 27; //so 27 -> 'AA'
18490  DirectionSuffix = ",A" + AnsiString(c);
18491  }
18492  else
18493  {
18494  DirectionSuffix = ",?"; //shouldn'tn ever get this far!
18495  }
18496  *SLIt = ServiceWithoutMarker + DirectionSuffix;
18497  }
18498  //now prepare the final consolidated output
18499  for(SLIt = ServiceList.begin(); SLIt != ServiceList.end(); SLIt++)
18500  {
18501  Output = Output + *SLIt + ","; //will end up with an unwanted comma at the end
18502  }
18503  if(Output.Length() > 0)
18504  {
18505  Output = Output.SubString(1, Output.Length() - 1); //remove the last comma
18506  }
18507  Utilities->CallLogPop(2216);
18508  return(Output);
18509  }
18510 
18511  catch(const Exception &e) //non error catch
18512  {
18513  AnalysisError = true;
18514  Utilities->CallLogPop(2227);
18515  return(e.Message);
18516  }
18517 }
18518 
18519 // ---------------------------------------------------------------------------
18520 
18521 AnsiString TTrainController::ConsolidateSARNTAtLoc(int Caller, const AnsiString Input, int &NumTrainsAtLoc)
18522 {
18523  //similar to above but doesn't include times in the input
18524  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ConsolidateSARNTAtLoc," + Input);
18525  AnsiString InternalInput = Input, Output = "", OneService = "";
18526  int CommaPos = 0;
18527  std::list<AnsiString> ServiceList;
18528  //load up the list
18529  while(InternalInput.Length() > 0)
18530  {
18531  CommaPos = InternalInput.Pos(',');
18532  if(CommaPos > 0) //0 if not found, as won't be when only one service left
18533  {
18534  OneService = InternalInput.SubString(1, CommaPos - 1);
18535  ServiceList.push_back(OneService);
18536  InternalInput = InternalInput.SubString(CommaPos + 1, InternalInput.Length() - CommaPos);
18537  }
18538  else //no comma so looking at last (or only) element
18539  {
18540  ServiceList.push_back(InternalInput);
18541  InternalInput = "";
18542  }
18543  }
18544 
18545  ServiceList.sort(); // alphabetical order
18546  ServiceList.unique(); //remove duplicates
18547  NumTrainsAtLoc = ServiceList.size(); //calc this after removing duplicates as may not have changed
18548  for(std::list<AnsiString>::iterator SLIt = ServiceList.begin(); SLIt != ServiceList.end(); SLIt++)
18549  {
18550  Output = Output + *SLIt + ","; //will end up with an unwanted comma at the end
18551  }
18552  if(Output.Length() > 0)
18553  {
18554  Output = Output.SubString(1, Output.Length() - 1); //remove the last comma
18555  }
18556  Utilities->CallLogPop(2217);
18557  return(Output);
18558 }
18559 
18560 // ---------------------------------------------------------------------------
18561 
18562 
18563 bool TTrainController::SameDirection(int Caller, AnsiString Ref1In, AnsiString Ref2In, AnsiString Time1, AnsiString Time2, int RepeatNum1, int RepeatNum2, TServiceCallingLocsList List1,
18564  TServiceCallingLocsList List2, AnsiString Location, bool Arrival)
18565 {
18566  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SameDirection," + Ref1In + "," + Ref2In + "," + Time1 + "," + Time2 + "," +
18567  AnsiString(RepeatNum1) + "," + AnsiString(RepeatNum2) + "," + Location);
18568 
18569  std::list<AnsiString>::iterator LP1 = 0, LP2 = 0, ListPtr1 = 0, ListPtr2 = 0, LocPtr1 = 0, LocPtr2 = 0; //LP1 & 2 are temporary pointers, ListPtrs are
18570  //general list pointers, LocPtrs point to Location in the two lists
18571 
18572  //first find the relevant values for LocPtr1 & LocPtr2 taking account of cdts and times
18573  //for List1
18574  bool LocFound = false;
18575  AnsiString Ref1 = Ref1In, Ref2 = Ref2In;
18576  int IncMinutes;
18577  TDateTime FirstServiceTime;
18578 
18579  //first need to strip off /1, /2 etc if present from Ref1 & Ref2 (leave Ref1In & Ref2In for error message & retain value as target in finding the correct reference for cdts)
18580  int Ref1Target = 0, Ref1Count = 0;
18581  int SlashPos = Ref1.Pos('/');
18582  if(SlashPos > 0) //if 0 Ref1 == Ref1In & target stays at 0
18583  {
18584  Ref1Target = Ref1.SubString(SlashPos + 1, Ref1.Length() - SlashPos).ToInt();
18585  Ref1 = Ref1.SubString(1, SlashPos - 1); //truncate up to but omit '/'
18586  }
18587  int Ref2Target = 0, Ref2Count = 0;
18588  SlashPos = Ref2.Pos('/');
18589  if(SlashPos > 0) //if 0 leave as is
18590  {
18591  Ref2Target = Ref2.SubString(SlashPos + 1, Ref2.Length() - SlashPos).ToInt();
18592  Ref2 = Ref2.SubString(1, SlashPos - 1); //truncate up to but omit '/'
18593  }
18594  for(ListPtr1 = List1.begin(); ListPtr1 != List1.end(); ListPtr1++) //note that when this routine entered Ref1In & Ref2In are already set to the correct services,
18595  {
18596  //even if others have same names. But if there are cdt's then need to refind the correct service
18597  if((*ListPtr1) == Location) //
18598  {
18599  LocPtr1 = ListPtr1; //may be modified later
18600  LocFound = true;
18601  }
18602  if(ListPtr1->SubString(1, 3) == "%%%")
18603  {
18604  AnsiString CDTTime = ListPtr1->SubString(4, 5);
18605  //now adjust the time to correspond to the repeat if there is one
18606  if(RepeatNum1 > 0) //if it is 0 then AnsiTime1 is already valid
18607  {
18608  IncMinutes = -1;
18609  FirstServiceTime = TDateTime(-1);
18610  bool BreakFlag = false;
18611  for(TTrainDataVector::iterator TDVIt = TrainDataVector.begin(); TDVIt != TrainDataVector.end(); TDVIt++)
18612  {
18613  if(TDVIt->ServiceReference == Ref1)
18614  {
18615  if(Ref1Target > Ref1Count)
18616  {
18617  Ref1Count++;
18618  continue;
18619  }
18620  IncMinutes = TDVIt->ActionVector.back().RearStartOrRepeatMins;
18621  for(TActionVector::iterator AVIt = TDVIt->ActionVector.begin(); AVIt != TDVIt->ActionVector.end(); AVIt++)
18622  {
18623  if(Utilities->Format96HHMM(AVIt->EventTime) == CDTTime)
18624  {
18625  FirstServiceTime = AVIt->EventTime; //i.e. the FirstService value of CDTTime
18626  BreakFlag = true;
18627  break;
18628  }
18629  if(Utilities->Format96HHMM(AVIt->ArrivalTime) == CDTTime) //add arr & dep in case find sooner (though dep shouldn't be sooner)
18630  {
18631  FirstServiceTime = AVIt->ArrivalTime;
18632  BreakFlag = true;
18633  break;
18634  }
18635  if(Utilities->Format96HHMM(AVIt->DepartureTime) == CDTTime)
18636  {
18637  FirstServiceTime = AVIt->DepartureTime;
18638  BreakFlag = true;
18639  break;
18640  }
18641  }
18642  if(BreakFlag)
18643  {
18644  break;
18645  }
18646  }
18647  }
18648  if(IncMinutes == -1)
18649  {
18650  throw Exception("Failed to find service for ServiceRef1 in SameDirection " + Ref1In + " " + Ref2In + " " + Time1 + " " + Time2 + " " + AnsiString(RepeatNum1) + " " + AnsiString(RepeatNum2) + " " + Location);
18651  }
18652  if(FirstServiceTime == TDateTime(-1))
18653  {
18654  throw Exception("Failed to find first service time for ServiceRef1 in SameDirection " + Ref1In + " " + Ref2In + " " + Time1 + " " + Time2 + " " + AnsiString(RepeatNum1) + " " + AnsiString(RepeatNum2) + " " + Location);
18655  }
18656  CDTTime = Utilities->Format96HHMM(TrainController->GetRepeatTime(60, FirstServiceTime, RepeatNum1, IncMinutes));
18657  }
18658  if(!Arrival && (Time1 == CDTTime)) //continue if equal in case next is a departure for the Location
18659  {
18660  LocFound = false;
18661  continue;
18662  }
18663  if(Arrival && (Time1 == CDTTime)) //gone far enough so can stop
18664  {
18665  break;
18666  }
18667  if(Time1 > CDTTime) //not there yet so go on
18668  {
18669  LocFound = false;
18670  continue;
18671  }
18672  if(Time1 < CDTTime) //gone too far so can stop now
18673  {
18674  break;
18675  }
18676  }
18677  }
18678  if(!LocFound) //have to find it in both lists
18679  {
18680  Utilities->CallLogPop(2228);
18681  return( false);
18682  }
18683  //for List2
18684  LocFound = false;
18685  for(ListPtr2 = List2.begin(); ListPtr2 != List2.end(); ListPtr2++)
18686  {
18687  if((*ListPtr2) == Location)
18688  {
18689  LocPtr2 = ListPtr2; //may be modified later
18690  LocFound = true;
18691  }
18692  if(ListPtr2->SubString(1, 3) == "%%%")
18693  {
18694  AnsiString CDTTime = ListPtr2->SubString(4, 5);
18695  //now adjust the time to correspond to the repeat if there is one
18696  if(RepeatNum2 > 0) //if it is 0 then AnsiTime1 is already valid
18697  {
18698  IncMinutes = -1;
18699  FirstServiceTime = TDateTime(-1);
18700  bool BreakFlag = false;
18701  for(TTrainDataVector::iterator TDVIt = TrainDataVector.begin(); TDVIt != TrainDataVector.end(); TDVIt++)
18702  {
18703  if(TDVIt->ServiceReference == Ref2)
18704  {
18705  if(Ref2Target > Ref2Count)
18706  {
18707  Ref2Count++;
18708  continue;
18709  }
18710  IncMinutes = TDVIt->ActionVector.back().RearStartOrRepeatMins;
18711  for(TActionVector::iterator AVIt = TDVIt->ActionVector.begin(); AVIt != TDVIt->ActionVector.end(); AVIt++)
18712  {
18713  if(Utilities->Format96HHMM(AVIt->EventTime) == CDTTime)
18714  {
18715  FirstServiceTime = AVIt->EventTime;
18716  BreakFlag = true;
18717  break;
18718  }
18719  if(Utilities->Format96HHMM(AVIt->ArrivalTime) == CDTTime)
18720  {
18721  FirstServiceTime = AVIt->ArrivalTime;
18722  BreakFlag = true;
18723  break;
18724  }
18725  if(Utilities->Format96HHMM(AVIt->DepartureTime) == CDTTime)
18726  {
18727  FirstServiceTime = AVIt->DepartureTime;
18728  BreakFlag = true;
18729  break;
18730  }
18731  }
18732  if(BreakFlag)
18733  {
18734  break;
18735  }
18736  }
18737  }
18738  if(IncMinutes == -1)
18739  {
18740  throw Exception("IncMinutes -1 for ServiceRef2 in SameDirection " + Ref1In + " " + Ref2In + " " + Time1 + " " + Time2 + " " + AnsiString(RepeatNum1) + " " + AnsiString(RepeatNum2) + " " + Location);
18741  }
18742  if(FirstServiceTime == TDateTime(-1))
18743  {
18744  throw Exception("First service time -1 for ServiceRef2 in SameDirection " + Ref1In + " " + Ref2In + " " + Time1 + " " + Time2 + " " + AnsiString(RepeatNum1) + " " + AnsiString(RepeatNum2) + " " + Location);
18745  }
18746  CDTTime = Utilities->Format96HHMM(TrainController->GetRepeatTime(61, FirstServiceTime, RepeatNum2, IncMinutes));
18747  }
18748  if(!Arrival && (Time2 == CDTTime)) //continue if equal in case next is a departure for the Location
18749  {
18750  LocFound = false;
18751  continue;
18752  }
18753  if(Arrival && (Time2 == CDTTime)) //gone far enough so can stop
18754  {
18755  break;
18756  }
18757  if(Time2 > CDTTime) //not there yet so go on
18758  {
18759  LocFound = false;
18760  continue;
18761  }
18762  if(Time2 < CDTTime) //gone too far so can stop now
18763  {
18764  break;
18765  }
18766  }
18767  }
18768  if(!LocFound) //have to find it in both lists, and should be found but allow for it not being
18769  {
18770  Utilities->CallLogPop(2229);
18771  return( false);
18772  }
18773  //now, for the arrival analysis, see if there is a common location before the LocPtrs & within any cdts, and if so return true, else return false
18774  //set ListPtr1 to the search start position
18775  if(Arrival)
18776  {
18777  LP1 = List1.begin();
18778  LP1--; //now points to before the first entry
18779  for(ListPtr1 = LocPtr1; ListPtr1 != LP1; ListPtr1--) //search backwards from Location
18780  {
18781  if(ListPtr1 == List1.begin())
18782  {
18783  break;
18784  }
18785  if(ListPtr1->SubString(1, 3) == "%%%") //a cdt event
18786  {
18787  ListPtr1++; //point to one past the cdt
18788  break;
18789  }
18790  }
18791  //set ListPtr2 to the search start position
18792  LP2 = List2.begin();
18793  LP2--; //now points to before the first entry
18794  for(ListPtr2 = LocPtr2; ListPtr2 != LP2; ListPtr2--)
18795  {
18796  if(ListPtr2 == List2.begin())
18797  {
18798  break;
18799  }
18800  if(ListPtr2->SubString(1, 3) == "%%%") //a cdt event
18801  {
18802  ListPtr2++; //point to one past the cdt
18803  break;
18804  }
18805  }
18806  //ListPtr1 & 2 now at search start position
18807  LP1 = ListPtr1;
18808  LP2 = ListPtr2;
18809  //now search forwards, i.e. for common locations before Location
18810  for(ListPtr1 = LP1; ListPtr1 != List1.end(); ListPtr1++)
18811  {
18812  if(ListPtr1 == LocPtr1) //reached Location without finding a common earlier location so skip to the backwards check
18813  {
18814  break;
18815  }
18816  if(ListPtr1->SubString(1, 3) == "%%%") //reached a cdt event without finding a common earlier location
18817  {
18818  break;
18819  }
18820  for(ListPtr2 = LP2; ListPtr2 != List2.end(); ListPtr2++)
18821  {
18822  if(ListPtr2 == LocPtr2) //not found common earlier location so go to the next ListPtr1
18823  {
18824  break;
18825  }
18826  if(ListPtr2->SubString(1, 3) == "%%%") //reached a cdt event without finding a common earlier location so go to the next ListPtr1
18827  {
18828  break;
18829  }
18830  if((*ListPtr1) == (*ListPtr2)) //found a common earlier location
18831  {
18832  Utilities->CallLogPop(2230);
18833  return( true);
18834  }
18835  }
18836  }
18837  }
18838 
18839  //now, for the departure analysis, reset the start positions and search locations after Location
18840 
18841  else
18842  {
18843  LP1 = LocPtr1;
18844  LP1++; //start at one past the location itself
18845  LP2 = LocPtr2;
18846  LP2++;
18847  for(ListPtr1 = LP1; ListPtr1 != List1.end(); ListPtr1++)
18848  {
18849  if(ListPtr1 == List1.end()) //reached end point so stop
18850  {
18851  break;
18852  }
18853  if(ListPtr1->SubString(1, 3) == "%%%") //reached a cdt event without finding a common location
18854  {
18855  break;
18856  }
18857  for(ListPtr2 = LP2; ListPtr2 != List2.end(); ListPtr2++)
18858  {
18859  if(ListPtr2 == List2.end()) //reached end point so go to next ListPtr1
18860  {
18861  break;
18862  }
18863  if(ListPtr2->SubString(1, 3) == "%%%") //reached a cdt event without finding a common location so go to the next ListPtr1
18864  {
18865  break;
18866  }
18867  if((*ListPtr1) == (*ListPtr2)) //found a common later location
18868  {
18869  Utilities->CallLogPop(2231);
18870  return( true);
18871  }
18872  }
18873  }
18874  }
18875  Utilities->CallLogPop(2232);
18876  return( false);
18877 }
18878 
18879 // ---------------------------------------------------------------------------
18880 
18881 AnsiString TTrainController::GetExitLocationAndAt(int Caller, TNumList &ExitList, AnsiString &AllowedExits) const
18882 {
18883  // changed at v2.7.0 to show allowable exit elements
18884  if(ExitList.empty())
18885  {
18886  return("");
18887  }
18888  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetExitLocationAndAt");
18889  AnsiString StartName = Track->TrackElementAt(735, *(ExitList.begin())).ActiveTrackElementName;
18890  AnsiString ExitLocList = "";
18891  AllowedExits = "";
18892 
18893  unsigned int Counter = 0;
18894  for(TNumListIterator ELIt = ExitList.begin(); ELIt != ExitList.end(); ELIt++)
18895  {
18896  ExitLocList += Track->TrackElementAt(1018, *ELIt).ElementID + " ";
18897  Counter++;
18898  if(((Counter % 6) == 0) && (Counter < (ExitList.size() - 1))) // only add a newline if more to come
18899  {
18900  ExitLocList += "\n";
18901  }
18902  }
18903  if(StartName == "")
18904  {
18905  if(ExitList.size() == 1)
18906  {
18907  AnsiString ID = Track->TrackElementAt(738, *(ExitList.begin())).ElementID;
18908  Utilities->CallLogPop(1571);
18909  return(" at " + ID);
18910  }
18911  else
18912  {
18913  Utilities->CallLogPop(1572);
18914  if(ExitList.size() < 4)
18915  {
18916  AllowedExits = ",\nallowable exit element(s): " + ExitLocList;
18917  return("");
18918  }
18919  else
18920  {
18921  AllowedExits = ",\nallowable exit element(s):\n" + ExitLocList;
18922  return("");
18923  }
18924  }
18925  }
18926  for(TNumListIterator ELIT = ExitList.begin(); ELIT != ExitList.end(); ELIT++)
18927  {
18928  if(Track->TrackElementAt(736, *ELIT).ActiveTrackElementName != StartName)
18929  {
18930  Utilities->CallLogPop(1570);
18931  if(ExitList.size() < 4)
18932  {
18933  AllowedExits = ",\nallowable exit element(s): " + ExitLocList;
18934  return("");
18935  }
18936  else
18937  {
18938  AllowedExits = ",\nallowable exit element(s):\n" + ExitLocList;
18939  return("");
18940  }
18941  }
18942  }
18943  Utilities->CallLogPop(1569);
18944  if(ExitList.size() < 4)
18945  {
18946  AllowedExits = ",\nallowable exit element(s): " + ExitLocList;
18947  return(" at " + StartName);
18948  }
18949  else
18950  {
18951  AllowedExits = ",\nallowable exit element(s):\n" + ExitLocList;
18952  return(" at " + StartName);
18953  }
18954 }
18955 
18956 // ---------------------------------------------------------------------------
18957 /* can't trust this as locations within a vector may not be contiguous
18958  bool TTrainController::IsServiceTerminating(int Caller, TTrainDataEntry *TDEPtr, TActionVectorEntry *AVPtr)
18959  {
18960  //Search ActionVector from the position after the entry value for Ptr to the end, and return true if find a Finish
18961  //entry before Fer or TimeLoc. No point checking for TimeTimeLoc since at a stop location now so a later TimeTimeLoc
18962  //must be preceded by a TimeLoc departure
18963  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",IsServiceTerminating");
18964  for(unsigned int x=1;x<TDEPtr->ActionVector.size();x++)
18965  {
18966  if((AVPtr + x) < TDEPtr->ActionVector.end())
18967  {
18968  AnsiString xx = (AVPtr + x)->Command;//test
18969  TTimetableFormatType xy = (AVPtr + x)->FormatType;//test
18970  TTimetableSequenceType xz = (AVPtr + x)->SequenceType;//test
18971  if(((AVPtr + x)->Command == "Fer") || ((AVPtr + x)->FormatType == TimeLoc))
18972  {
18973  Utilities->CallLogPop();
18974  return false;
18975  }
18976  else if((AVPtr + x)->SequenceType == Finish)
18977  {
18978  Utilities->CallLogPop();
18979  return true;
18980  }
18981  }
18982  }
18983  Utilities->CallLogPop();
18984  return false;
18985  }
18986 */
18987 // ---------------------------------------------------------------------------
18988 
18989 void TTrainController::SendPerformanceSummary(int Caller, std::ofstream &PerfFile)
18990 {
18991  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SendPerformanceSummary");
18992  AnsiString FormatStr = "####0.0";
18993  AnsiString AvLateArrMins = "";
18994  AnsiString AvEarlyArrMins = "";
18995  AnsiString AvLatePassMins = "";
18996  AnsiString AvEarlyPassMins = "";
18997  AnsiString AvLateDepMins = "";
18998  AnsiString AvLateExitMins = "";
18999  AnsiString AvEarlyExitMins = "";
19000 
19001  if(LateArrivals > 0)
19002  {
19003  AvLateArrMins = FormatFloat(FormatStr, (TotLateArrMins / LateArrivals));
19004  }
19005  if(EarlyArrivals > 0)
19006  {
19007  AvEarlyArrMins = FormatFloat(FormatStr, (TotEarlyArrMins / EarlyArrivals));
19008  }
19009  if(LatePasses > 0)
19010  {
19011  AvLatePassMins = FormatFloat(FormatStr, (TotLatePassMins / LatePasses));
19012  }
19013  if(EarlyPasses > 0)
19014  {
19015  AvEarlyPassMins = FormatFloat(FormatStr, (TotEarlyPassMins / EarlyPasses));
19016  }
19017  if(LateDeps > 0)
19018  {
19019  AvLateDepMins = FormatFloat(FormatStr, (TotLateDepMins / LateDeps));
19020  }
19021  if(LateExits > 0) //added at v2.9.1
19022  {
19023  AvLateExitMins = FormatFloat(FormatStr, (TotLateExitMins / LateExits));
19024  }
19025  if(EarlyExits > 0) //added at v2.9.1
19026  {
19027  AvEarlyExitMins = FormatFloat(FormatStr, (TotEarlyExitMins / EarlyExits));
19028  }
19029  PerfFile << '\n' << '\n' << "***************************************";
19030  PerfFile << '\n' << '\n' << "Performance summary:" << '\n';
19031 
19032  if(OnTimeArrivals != 1)
19033  {
19034  PerfFile << OnTimeArrivals << " on-time arrivals" << '\n';
19035  }
19036  else
19037  {
19038  PerfFile << OnTimeArrivals << " on-time arrival" << '\n';
19039  }
19040  if(LateArrivals > 1)
19041  {
19042  PerfFile << LateArrivals << " late arrivals (average " << AvLateArrMins.c_str() << " min)" << '\n';
19043  }
19044  else if(LateArrivals == 1)
19045  {
19046  PerfFile << LateArrivals << " late arrival (" << AvLateArrMins.c_str() << " min)" << '\n';
19047  }
19048  else
19049  {
19050  PerfFile << LateArrivals << " late arrivals" << '\n';
19051  }
19052  if(EarlyArrivals > 1)
19053  {
19054  PerfFile << EarlyArrivals << " early arrivals (average " << AvEarlyArrMins.c_str() << " min)" << '\n';
19055  }
19056  else if(EarlyArrivals == 1)
19057  {
19058  PerfFile << EarlyArrivals << " early arrival (" << AvEarlyArrMins.c_str() << " min)" << '\n';
19059  }
19060  else
19061  {
19062  PerfFile << EarlyArrivals << " early arrivals" << '\n';
19063  }
19064  if(OnTimePasses != 1)
19065  {
19066  PerfFile << OnTimePasses << " on-time passes" << '\n';
19067  }
19068  else
19069  {
19070  PerfFile << OnTimePasses << " on-time pass" << '\n';
19071  }
19072  if(LatePasses > 1)
19073  {
19074  PerfFile << LatePasses << " late passes (average " << AvLatePassMins.c_str() << " min)" << '\n';
19075  }
19076  else if(LatePasses == 1)
19077  {
19078  PerfFile << LatePasses << " late pass (" << AvLatePassMins.c_str() << " min)" << '\n';
19079  }
19080  else
19081  {
19082  PerfFile << LatePasses << " late passes" << '\n';
19083  }
19084  if(EarlyPasses > 1)
19085  {
19086  PerfFile << EarlyPasses << " early passes (average " << AvEarlyPassMins.c_str() << " min)" << '\n';
19087  }
19088  else if(EarlyPasses == 1)
19089  {
19090  PerfFile << EarlyPasses << " early pass (" << AvEarlyPassMins.c_str() << " min)" << '\n';
19091  }
19092  else
19093  {
19094  PerfFile << EarlyPasses << " early passes" << '\n';
19095  }
19096 
19097  if(OnTimeExits != 1) //this batch added at v2.9.1
19098  {
19099  PerfFile << OnTimeExits << " on-time exits" << '\n';
19100  }
19101  else
19102  {
19103  PerfFile << OnTimeExits << " on-time exit" << '\n';
19104  }
19105  if(LateExits > 1)
19106  {
19107  PerfFile << LateExits << " late exits (average " << AvLateExitMins.c_str() << " min)" << '\n';
19108  }
19109  else if(LateExits == 1)
19110  {
19111  PerfFile << LateExits << " late exit (" << AvLateExitMins.c_str() << " min)" << '\n';
19112  }
19113  else
19114  {
19115  PerfFile << LateExits << " late exits" << '\n';
19116  }
19117  if(EarlyExits > 1)
19118  {
19119  PerfFile << EarlyExits << " early exits (average " << AvEarlyExitMins.c_str() << " min)" << '\n';
19120  }
19121  else if(EarlyExits == 1)
19122  {
19123  PerfFile << EarlyExits << " early exit (" << AvEarlyExitMins.c_str() << " min)" << '\n';
19124  }
19125  else
19126  {
19127  PerfFile << EarlyExits << " early exits" << '\n';
19128  }
19129 
19130  if(OnTimeDeps != 1)
19131  {
19132  PerfFile << OnTimeDeps << " on-time departures" << '\n';
19133  }
19134  else
19135  {
19136  PerfFile << OnTimeDeps << " on-time departure" << '\n';
19137  }
19138  if(LateDeps > 1)
19139  {
19140  PerfFile << LateDeps << " late departures (average " << AvLateDepMins.c_str() << " min)" << '\n';
19141  }
19142  else if(LateDeps == 1)
19143  {
19144  PerfFile << LateDeps << " late departure (" << AvLateDepMins.c_str() << " min)" << '\n';
19145  }
19146  else
19147  {
19148  PerfFile << LateDeps << " late departures" << '\n';
19149  }
19150  TDateTime TempExcessLCDownTime;
19151  for(unsigned int x = 0; x < Track->BarriersDownVector.size(); x++) //added at v2.6.0 - should have been added earlier
19152  {
19153 // if(Track->BarriersDownVector.at(x).ReducedTimePenalty) //assume train still to cross LC as probably will, else have false high value & can have
19154  //later perf summaries with lower values, changed at v2.8.0
19155 // {
19156  TempExcessLCDownTime = TrainController->TTClockTime - Track->BarriersDownVector.at(x).StartTime - TDateTime(180.0 / 86400);
19157 // }
19158 /*
19159  else
19160  {
19161  TempExcessLCDownTime = TrainController->TTClockTime - Track->BarriersDownVector.at(x).StartTime;
19162  }
19163 */
19164  if(TempExcessLCDownTime > TDateTime(0))
19165  {
19166  TrainController->ExcessLCDownMins += (double(TempExcessLCDownTime) * 1440);
19167  }
19168  }
19169 
19170  AnsiString FormattedExcessLCDownMins = FormatFloat(FormatStr, ExcessLCDownMins);
19171 
19172  if(ExcessLCDownMins > 0.1)
19173  {
19174  PerfFile << FormattedExcessLCDownMins.c_str() << " excess minutes of level crossing barrier down time" << '\n';
19175  }
19176  if(MissedStops != 1)
19177  {
19178  PerfFile << MissedStops << " missed stops" << '\n';
19179  }
19180  else
19181  {
19182  PerfFile << MissedStops << " missed stop" << '\n';
19183  }
19184  if(OtherMissedEvents != 1)
19185  {
19186  PerfFile << OtherMissedEvents << " other missed events" << '\n';
19187  }
19188  else
19189  {
19190  PerfFile << OtherMissedEvents << " other missed event" << '\n';
19191  }
19192  if(SkippedTTEvents != 1)
19193  {
19194  PerfFile << SkippedTTEvents << " skipped timetable events" << '\n';
19195  }
19196  else
19197  {
19198  PerfFile << SkippedTTEvents << " skipped timetable event" << '\n';
19199  }
19200  if(UnexpectedExits != 1)
19201  {
19202  PerfFile << UnexpectedExits << " unexpected train exits" << '\n';
19203  }
19204  else
19205  {
19206  PerfFile << UnexpectedExits << " unexpected train exit" << '\n';
19207  }
19208  if(IncorrectExits != 1)
19209  {
19210  PerfFile << IncorrectExits << " incorrect train exits" << '\n';
19211  }
19212  else
19213  {
19214  PerfFile << IncorrectExits << " incorrect train exit" << '\n';
19215  }
19216  if(NumFailures != 1)
19217  {
19218  PerfFile << NumFailures << " train failures" << '\n';
19219  }
19220  else
19221  {
19222  PerfFile << NumFailures << " train failure" << '\n';
19223  }
19224  if(AvHoursIntValue > 0)
19225  {
19226  if(AvHoursIntValue == 1)
19227  {
19228  PerfFile << AvHoursIntValue << " hour mean time betweeen train failures" << '\n';
19229  }
19230  else
19231  {
19232  PerfFile << AvHoursIntValue << " hours mean time betweeen train failures" << '\n';
19233  }
19234  }
19235  AnsiString AvLateMinsLocsNotReached = "";
19236 
19238  int LocsNotReached = (NotStartedTrainLateArr + OperatingTrainLateArr); //dropped divide by 2 after 2.7.0 as don't count late departures as 'failed to arrive'
19239  // each location has an arrival and departure (generally) so divide by 2 - no, dropped after 2.7.0
19240 
19241  if(LocsNotReached > 0)
19242  {
19243  AvLateMinsLocsNotReached = FormatFloat(FormatStr, (OperatingTrainLateMins + NotStartedTrainLateMins) / (NotStartedTrainLateArr + OperatingTrainLateArr));
19244  PerfFile << LocsNotReached << " locations that trains failed to reach (average lateness " << AvLateMinsLocsNotReached.c_str() << " min)" << '\n';
19245  }
19246  if(SPADRisks != 1)
19247  {
19248  PerfFile << SPADRisks << " SPAD risks" << '\n';
19249  }
19250  else
19251  {
19252  PerfFile << SPADRisks << " SPAD risk" << '\n';
19253  }
19254  if(SPADEvents != 1)
19255  {
19256  PerfFile << SPADEvents << " SPADs" << '\n';
19257  }
19258  else
19259  {
19260  PerfFile << SPADEvents << " SPAD" << '\n';
19261  }
19262  if(Derailments != 1)
19263  {
19264  PerfFile << Derailments << " derailments" << '\n';
19265  }
19266  else
19267  {
19268  PerfFile << Derailments << " derailment" << '\n';
19269  }
19270  if(CrashedTrains != 1)
19271  {
19272  PerfFile << CrashedTrains << " crashed trains" << '\n';
19273  }
19274  else
19275  {
19276  PerfFile << CrashedTrains << " crashed train" << '\n';
19277  }
19278  PerfFile << '\n' << "***************************************" << '\n';
19279 
19280  bool DerailSPADFlag = false, CrashFlag = false;
19281 
19282  int OverallScorePercent = 100;
19283  int TotArrDepExit = 0;
19284  double TotLateMinsFactor = 1;
19285  double MissedStopAndSPADRiskFactor = 1;
19286  double NetNegFactor = 1;
19287 
19289  EarlyExits + LateExits + OnTimeExits; //exits added at v2.9.1, passes not counted
19290  // TotArrDep: total number of arrivals & departures including those for trains that haven't reached their destinations yet and are late
19291  // changed at v1.1.4 - calc was inside "if(OverallScorePercent == 100).." block so could remain 0 for SPADs & crashes, & then received the
19292  // 'no timetabled departures... message, which was inappropriate
19293 
19294  if((SPADEvents > 0) || (Derailments > 0))
19295  {
19296  OverallScorePercent = 5; // overrides other calculations
19297  DerailSPADFlag = true;
19298  }
19299  if(CrashedTrains > 0)
19300  {
19301  OverallScorePercent = 0; // overrides other calculations
19302  CrashFlag = true;
19303  }
19304  if(OverallScorePercent == 100)
19305  {
19306  if(TotArrDepExit > 0)
19307  {
19308  TotLateMinsFactor =
19310  ((OtherMissedEvents + SkippedTTEvents + UnexpectedExits + ExcessLCDownMins) * 15)) / TotArrDepExit); //exits added at v2.9.1
19311  // TotLateMinsFactor: negative exponential factor based on overall average arr & dep minutes late (with OtherMissedEvents & UnexpectedExits
19312  // counting as 15 mins late each), where 4 mins late average = half, 8 mins late = a quarter etc
19313  MissedStopAndSPADRiskFactor = exp((-17.33) * (MissedStops + SPADRisks + IncorrectExits) / TotArrDepExit);
19314  // MissedEventAndSPADRiskFactor: negative exponential factor based on number of missed stops, SPAD risks & IncorrectExits as a proportion
19315  // of arrivals & departures, where 4% = half, 8% = a quarter etc
19316  NetNegFactor = TotLateMinsFactor * MissedStopAndSPADRiskFactor;
19317  // NetNegfactor: product of the above two
19318  OverallScorePercent = 100 * NetNegFactor;
19319  }
19320  }
19321  if((TotArrDepExit > 0) || DerailSPADFlag || CrashFlag)
19322  // flag condits added at v1.1.4 - see above for what the error was
19323  {
19324  AnsiString OneFailureString = ", though the failure would account for some poor performance";
19325  AnsiString TwoOrMoreFailureString = ", though the failures would account for some poor performance";
19326  AnsiString AddedString = "";
19327  if(NumFailures == 1)
19328  {
19329  AddedString = OneFailureString;
19330  }
19331  if(NumFailures > 1)
19332  {
19333  AddedString = TwoOrMoreFailureString;
19334  }
19335  PerfFile << "\nOverall score: " << OverallScorePercent << "%\n";
19336  AnsiString Rating = "";
19337  if(OverallScorePercent == 100)
19338  {
19339  Rating = "Perfect!";
19340  }
19341  else if(OverallScorePercent >= 95)
19342  {
19343  Rating = "Excellent";
19344  }
19345  else if(OverallScorePercent >= 90)
19346  {
19347  Rating = "Very good";
19348  }
19349  else if(OverallScorePercent >= 80)
19350  {
19351  Rating = "Good";
19352  }
19353  else if(OverallScorePercent >= 70)
19354  {
19355  Rating = "Fair";
19356  }
19357  else if(OverallScorePercent >= 60)
19358  {
19359  Rating = "Unacceptable" + AddedString;
19360  }
19361  else if(OverallScorePercent >= 50)
19362  {
19363  Rating = "Poor" + AddedString;
19364  }
19365  else if(OverallScorePercent >= 40)
19366  {
19367  Rating = "Bad" + AddedString;
19368  }
19369  else if(OverallScorePercent >= 30)
19370  {
19371  Rating = "Very bad" + AddedString;
19372  }
19373  else if(OverallScorePercent >= 20)
19374  {
19375  Rating = "Terrible" + AddedString;
19376  }
19377  else if(OverallScorePercent >= 10)
19378  {
19379  Rating = "Appalling" + AddedString;
19380  }
19381  else if(OverallScorePercent >= 5)
19382  {
19383  if(DerailSPADFlag)
19384  {
19385  Rating = "Disastrous - potential loss of life";
19386  }
19387  // SPADs/Derailments
19388  else
19389  {
19390  Rating = "Dire" + AddedString;
19391  }
19392  }
19393  else if(OverallScorePercent < 5)
19394  {
19395  if(CrashFlag)
19396  {
19397  Rating = "Catastrophic - loss of life"; // Crashes
19398  }
19399  else
19400  {
19401  Rating = "Abysmal";
19402  }
19403  }
19404  PerfFile << "Overall rating: " << Rating.c_str() << '\n';
19405  }
19406  else
19407  {
19408  PerfFile << "\nThere were no timetabled departures, arrivals or exits so there is insufficient information to provide a performance score or rating" << '\n';
19409  }
19410  PerfFile << '\n' << "***************************************";
19411  Utilities->CallLogPop(1736);
19412 }
19413 
19414 // ---------------------------------------------------------------------------
19415 
19417 {
19418  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SetWarningFlags");
19419  for(unsigned int x = 0; x < TrainVector.size(); x++)
19420  {
19421  TTrain &Train = TrainVectorAt(58, x);
19422  if(Train.Crashed)
19423  // can't use background colours for crashed & derailed because same colour
19424  {
19425  CrashWarning = true;
19426  }
19427  else if(Train.Derailed)
19428  // can't use background colours for crashed & derailed because same colour
19429  {
19430  DerailWarning = true;
19431  }
19432  else if(Train.BackgroundColour == clSPADBackground)
19433  // use colour as that changes as soon as passes signal
19434  {
19435  SPADWarning = true;
19436  }
19437  else if(Train.BackgroundColour == clTrainFailedBackground)
19438  {
19439  TrainFailedWarning = true;
19440  }
19441  else if(Train.BackgroundColour == clCallOnBackground)
19442  // use colour as also stopped at signal
19443  {
19444  CallOnWarning = true;
19445  }
19446  else if(Train.BackgroundColour == clSignalStopBackground)
19447  // use colour to distinguish from call-on
19448  {
19449  SignalStopWarning = true;
19450  }
19451  else if(Train.BackgroundColour == clBufferAttentionNeeded)
19452  // use colour to distinguish from ordinary buffer stop
19453  {
19454  BufferAttentionWarning = true;
19455  }
19456  }
19457  Utilities->CallLogPop(1796);
19458 }
19459 
19460 // ---------------------------------------------------------------------------
19461 
19463 {
19464  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CalcSignalStopLateness");
19465 
19466  // calculate lateness for running trains
19469  for(unsigned int x = 0; x < TrainVector.size(); x++)
19470  {
19471  TTrain &Train = TrainVectorAt(64, x);
19472  for(TActionVectorEntry * AVEntryPtr = &Train.TrainDataEntryPtr->ActionVector.front(); AVEntryPtr < &Train.TrainDataEntryPtr->ActionVector.back();
19473  AVEntryPtr++)
19474  {
19475  if(AVEntryPtr < Train.ActionVectorEntryPtr)
19476  {
19477  continue;
19478  }
19479  if((AVEntryPtr->ArrivalTime > TDateTime(-1)) && !Train.StoppedAtLocation && (GetRepeatTime(42, AVEntryPtr->ArrivalTime, Train.RepeatNumber, Train.IncrementalMinutes) <
19480  TTClockTime))
19481  {
19482  OperatingTrainLateMins += 1440 * double(TTClockTime - GetRepeatTime(43, AVEntryPtr->ArrivalTime, Train.RepeatNumber, Train.IncrementalMinutes));
19484  }
19485 /* dropped departures after 2.7.0 because these don't count for 'failed to reach' numbers
19486  if((AVEntryPtr->DepartureTime > TDateTime(-1)) && (GetRepeatTime(44, AVEntryPtr->DepartureTime, Train.RepeatNumber, Train.IncrementalMinutes) <
19487  TTClockTime))
19488  {
19489  OperatingTrainLateMins += 1440 * double(TTClockTime - GetRepeatTime(45, AVEntryPtr->DepartureTime, Train.RepeatNumber, Train.IncrementalMinutes));
19490  OperatingTrainArrDep++;
19491  }
19492 */
19493  }
19494  }
19495 
19496  // calculate lateness for trains that haven't started yet (could be held awaiting entry)
19499 
19500  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
19501  {
19502  TTrainDataEntry & TDEntry = TrainDataVector.at(x);
19503  const TActionVectorEntry &AVEntryLast = TDEntry.ActionVector.at(TDEntry.ActionVector.size() - 1);
19504  int IncrementalMinutes = 0;
19505  if(AVEntryLast.FormatType == Repeat)
19506  {
19507  IncrementalMinutes = AVEntryLast.RearStartOrRepeatMins;
19508  }
19509  for(int y = 0; y < TDEntry.NumberOfTrains; y++)
19510  {
19511  TTrainOperatingData &TTOD = TDEntry.TrainOperatingDataVector.at(y);
19512  if(TTOD.RunningEntry != NotStarted)
19513  {
19514  continue;
19515  }
19516  // note that can't rely on the above for sessionfiles saved before v0.6b as wasn't set to Running for Sns/Fsp/rsp & shuttles
19517  // but if trains had exited then would be set to Exited, so need to check against trains still operating - use the test below
19518  bool TrainOperatingFlag = false;
19519  for(unsigned int a = 0; a < TrainController->TrainVector.size(); a++)
19520  {
19521  if((TrainController->TrainVector.at(a).TrainDataEntryPtr == &TDEntry) && (TrainController->TrainVector.at(a).RepeatNumber == y))
19522  {
19523  TrainOperatingFlag = true;
19524  break;
19525  }
19526  }
19527  if(TrainOperatingFlag)
19528  {
19529  continue;
19530  }
19531  if(GetRepeatTime(46, TDEntry.ActionVector.at(0).EventTime, y, IncrementalMinutes) > TTClockTime)
19532  {
19533  break; // if the first time is greater than TTClockTime then all the rest will also be greater (& default of -1 will be less so will be ignored)
19534  }
19535  for(unsigned int z = 0; z < TDEntry.ActionVector.size(); z++)
19536  {
19537  TActionVectorEntry &AVEntry = TDEntry.ActionVector.at(z);
19538  if(GetRepeatTime(35, AVEntry.EventTime, y, IncrementalMinutes) > TTClockTime)
19539  {
19540  break; // all the rest will also be greater (& default of -1 will be less)
19541  }
19542  if(GetRepeatTime(36, AVEntry.ArrivalTime, y, IncrementalMinutes) > TTClockTime)
19543  {
19544  break; // all the rest will also be greater (& default of -1 will be less)
19545  }
19546  if(GetRepeatTime(37, AVEntry.DepartureTime, y, IncrementalMinutes) > TTClockTime)
19547  {
19548  break; // all the rest will also be greater (& default of -1 will be less)
19549  }
19550  if((AVEntry.ArrivalTime > TDateTime(-1)) && (GetRepeatTime(38, AVEntry.ArrivalTime, y, IncrementalMinutes) < TTClockTime))
19551  {
19552  NotStartedTrainLateMins += 1440 * double(TTClockTime - GetRepeatTime(39, AVEntry.ArrivalTime, y, IncrementalMinutes));
19554  }
19555 /* dropped departures after 2.7.0 as only interested in 'failed to reach' number - if train hasn't arrived then it hasn't departed so shouldn't count that as part of 'failed to reach'
19556  if((AVEntry.DepartureTime > TDateTime(-1)) && (GetRepeatTime(40, AVEntry.DepartureTime, y, IncrementalMinutes) < TTClockTime))
19557  {
19558  NotStartedTrainLateMins += 1440 * double(TTClockTime - GetRepeatTime(41, AVEntry.DepartureTime, y, IncrementalMinutes));
19559  NotStartedTrainArrDep++;
19560  }
19561 */
19562  }
19563  }
19564  }
19565  Utilities->CallLogPop(1894);
19566 }
19567 
19568 // ---------------------------------------------------------------------------
19569 
19571 // new v2.2.0 for OperatorActionPanel
19572 // clears entries then adds values for running trains then for continuation entries
19573 // dont limit size here as need to check all trains (OAListBox is limited to 20 trains in Interface.cpp)
19574 {
19575  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",RebuildOpTimeToActMultimap");
19576  OpTimeToActMultiMap.clear();
19577  TOpTimeToActMultiMapEntry OpTimeToActMultiMapEntry;
19578 
19579  if(!TrainVector.empty())
19580  // build OpTimeToActMultiMap entries for running trains
19581  {
19582  AnsiString HeadCode;
19583  // dropped in favour of TrainID for running trains int VecPos; //TrackVectorPosition of LeadElement or continuation where train is to enter
19584  int TrainID;
19585  THCandTrainPosParam HCandTrainPosParam;
19586  for(unsigned int x = 0; x < TrainVector.size(); x++)
19587  {
19588  HeadCode = TrainVectorAt(62, x).HeadCode;
19589  TrainID = TrainVectorAt(63, x).TrainID;
19590  HCandTrainPosParam.first = HeadCode;
19591  HCandTrainPosParam.second = TrainID;
19592  float TimeToAct = TrainVectorAt(65, x).OpTimeToAct;
19593  if((TimeToAct >= 0) && (TimeToAct < 59.9))
19594  // -1 indicates don't display
19595  {
19596  OpTimeToActMultiMapEntry.first = TimeToAct;
19597  OpTimeToActMultiMapEntry.second = HCandTrainPosParam;
19598  OpTimeToActMultiMap.insert(OpTimeToActMultiMapEntry);
19599  }
19600  }
19601  }
19602 /*
19603  * class TContinuationTrainExpectationEntry
19604  {
19605  public:
19606  AnsiString Description; ///< service description
19607  AnsiString HeadCode; ///< service headcode
19608  int RepeatNumber; ///< service RepeatNumber
19609  int IncrementalMinutes; ///< Repeat separation in minutes
19610  int IncrementalDigits; ///< Repeat headcode separation
19611  int VectorPosition; ///< TrackVectorPosition for the continuation element
19612  TTrainDataEntry *TrainDataEntryPtr; ///< points to the service entry in the timetable's TrainDataVector
19613  };
19614 
19615  Multimap class for TContinuationTrainExpectationEntry objects, where the access key is the expectation time
19616  typedef std::multimap<TDateTime, TContinuationTrainExpectationEntry> TContinuationTrainExpectationMultiMap;
19617  typedef TContinuationTrainExpectationMultiMap::iterator TContinuationTrainExpectationMultiMapIterator; ///< iterator for the multimap
19618  typedef std::pair<TDateTime, TContinuationTrainExpectationEntry> TContinuationTrainExpectationMultiMapPair; ///< a single multimap entry
19619 */
19620 
19622  // build OpTimeToActMultiMap entries for expected trains
19623  {
19624  // note that using the ContinuationTrainExpectationMultiMap automatically ensures that entries are in ascending time order
19625  // first have to calculate times to red signal for each train due to enter (ignore later trains as will likely change before they are due)
19626  float TimeToAct = 0; // minutes
19627  int DistanceToRedSignal = 0; // metres
19628  TContinuationEntryVecPosVector ContinuationEntryVecPosVector;
19629  // used to ensure only one train displayed for a given continuation
19630  ContinuationEntryVecPosVector.clear();
19631  bool LaterTrain = false;
19634  {
19635  LaterTrain = false;
19636  if(CTEIt->second.TrainDataEntryPtr->TrainOperatingDataVector.at(CTEIt->second.RepeatNumber).RunningEntry != NotStarted)
19637  {
19638  CTEIt++;
19639  continue; // not interested in running or exited trains
19640  }
19641  if(Track->TrackElementAt(934, CTEIt->second.VectorPosition).TrainIDOnElement > 0)
19642  {
19643  CTEIt++;
19644  continue;
19645  // don't include trains not entered yet when a train is already on the continuation
19646  }
19647  if(!ContinuationEntryVecPosVector.empty())
19648  {
19649  for(unsigned int x = 0; x < ContinuationEntryVecPosVector.size(); x++)
19650  {
19651  if(CTEIt->second.VectorPosition == ContinuationEntryVecPosVector.at(x))
19652  {
19653  LaterTrain = true;
19654  ;
19655  // skip past remaining trains waiting to enter at same point
19656  break;
19657  }
19658  }
19659  }
19660  if(LaterTrain)
19661  {
19662  CTEIt++;
19663  continue;
19664  }
19665  ContinuationEntryVecPosVector.push_back(CTEIt->second.VectorPosition);
19666  AnsiString HeadCode = CTEIt->second.HeadCode;
19667  float CurrentStopTime; // set to 0 at start of function
19668  float LaterStopTime; // set to 0 at start of function
19669  float RecoverableTime; // set to 0 at start of function
19670  int AvTrackSpeed; // set to 0 at start of function
19671  int TrainID = -1; // not yet allocated for train still to enter
19672  int DistanceToExit; //not used for continuation entries
19673  THVShortPair ExitPair;
19674  bool SigControlAndCanPassRedSignal = false; // doesn't apply for a continuation
19675 
19676  DistanceToRedSignal = CalcDistanceToRedSignalandStopTime(1, CTEIt->second.VectorPosition, 0,
19677  // EntryPos always 0 for entering at a continuation
19678  SigControlAndCanPassRedSignal, &CTEIt->second.TrainDataEntryPtr->ActionVector.at(0), //changed to ...at(0)
19679  //from at(1) at v2.11.0 as signaller control threw an error - found when opening ActionsDue panel with B'ham (error had been present for some time)
19680  HeadCode, TrainID, CurrentStopTime, LaterStopTime, RecoverableTime, AvTrackSpeed, DistanceToExit, ExitPair);
19681  // for above VectorPosition is the first element to have its length included in the sum, so for a continuation it's the continuation itself
19682  // for a train it's the one in front of LeadElement
19683  if(AvTrackSpeed < 30)
19684  {
19685  AvTrackSpeed = 30;
19686  }
19687  if(DistanceToRedSignal == -1)
19688  {
19689  TimeToAct = 60.0;
19690  }
19691  else
19692  {
19693  int Speed = AvTrackSpeed;
19694  int MaxSpeed = int(CTEIt->second.TrainDataEntryPtr->MaxRunningSpeed);
19695  if(AvTrackSpeed > MaxSpeed)
19696  {
19697  Speed = MaxSpeed;
19698  }
19699  if(CTEIt->second.TrainDataEntryPtr->ActionVector.at(0).SignallerControl) //changed to ...at(0) from at(1) at v2.11.0 as SignallerControl only valid for ..at(0)
19700  // defined in timetable as under signaller control
19701  {
19702  Speed = CTEIt->second.TrainDataEntryPtr->SignallerSpeed;
19703  LaterStopTime = 0;
19704  }
19705  TimeToAct = LaterStopTime + DistanceToRedSignal * 3.6 / 60 / Speed;
19706  // accel & decel taken into account in
19707  // CalcDistanceToRedSignalandStopTime
19708  // 3.6 convertsKm/h to m/s & 60 converts seconds to minutes
19709  // don't need CurrentStopTime or RecoverableTime for continuation entries
19710  float MinsBefEnter = double(CTEIt->first - TTClockTime) * 86400.0 / 60.0;
19711  TimeToAct += MinsBefEnter;
19712  }
19713  THCandTrainPosParam HCandTrainPosParam;
19714  HCandTrainPosParam.first = HeadCode;
19715  HCandTrainPosParam.second = -1 - CTEIt->second.VectorPosition;
19716  // -1-CTE... because 2nd value covers TrainID if +ve &
19717  // continuation track vector position if -ve, -1 allows for vecpos being 0
19718  if(TimeToAct < 59.9) // if 60 don't enter a value in multimap
19719  {
19720  OpTimeToActMultiMapEntry.first = TimeToAct;
19721  OpTimeToActMultiMapEntry.second = HCandTrainPosParam;
19722  OpTimeToActMultiMap.insert(OpTimeToActMultiMapEntry);
19723  }
19724  CTEIt++;
19725  }
19726  }
19727  Utilities->CallLogPop(2081);
19728 }
19729 
19730 // ---------------------------------------------------------------------------
19731 
19733 // new for multiplayer
19734 // clears entries then adds values for running trains
19735 {
19736  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",RebuildTimeToExitMultiMap");
19737  TimeToExitMultiMap.clear();
19738  TTimeToExitMultiMapEntry TimeToExitMultiMapEntry;
19739 
19740  if(!TrainVector.empty())
19741  // build map entries for running trains
19742  {
19743  TExitInfo ExitInfo; //corresponds to TServiceInfo in Interface
19744  THVShortPair ExitPair;
19745  float TimeToExit;
19746  for(unsigned int x = 0; x < TrainVector.size(); x++)
19747  {
19749  ExitInfo.RepeatNumber = short(TrainVectorAt(81, x).RepeatNumber);
19750  ExitInfo.TimeToExitSecs = short(TrainVectorAt(77, x).TimeToExit * 60);
19751  ExitPair = TrainVectorAt(76, x).ExitPair;
19752  if((ExitInfo.TimeToExitSecs >= 3570) || (ExitInfo.TimeToExitSecs < 1)) //59.5 mins or -60 secs
19753  {
19754  ExitInfo.TimeToExitSecs = -1;
19755  }
19756  TimeToExitMultiMapEntry.first = ExitPair;
19757  TimeToExitMultiMapEntry.second = ExitInfo;
19758  TimeToExitMultiMap.insert(TimeToExitMultiMapEntry);
19759  }
19760  }
19761  Utilities->CallLogPop(2323);
19762 }
19763 
19764 // ---------------------------------------------------------------------------
19765 
19766 int TTrainController::CalcDistanceToRedSignalandStopTime(int Caller, int TrackVectorPosition, int TrackVectorPositionEntryPos,
19767  bool SigControlAndCanPassRedSignal, TActionVectorEntry *AVPtr, AnsiString HeadCode, int TrainID, float &CurrentStopTime, float &LaterStopTime,
19768  float &RecoverableTime, int &AvTrackSpeed, int &DistanceToExit, THVShortPair &ExitPair)
19769 // new v2.2.0
19770 // vectorPosition is the value for the first element to be measured - for a continuation it's the continuation itself, for a train
19771 // it's the one after LeadElement, returns -1 for infinity - i.e. not approaching red signal (e.g. maybe cdt before reach it).
19772 // CurrentStopTime is the time to depart from the current station (if stopped at a station) and LaterStopTime is the total station
19773 // stop times for stations after the current one. DistanceToRedSignal is what the name implies, if -1 is returned the other values
19774 // aren't used - this means there is no display for the train in question
19775 {
19776  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",DistanceToRedSignal, " + AnsiString(TrackVectorPosition) + ", " +
19777  AnsiString(TrackVectorPositionEntryPos) + ", " + AVPtr->Command);
19778  int DistanceToRedSignal = 0;
19779  DistanceToExit = -1;
19780  ExitPair.first = -1;
19781  ExitPair.second = -1;
19782  int CumTrackSpeed = 0;
19783  // average track speed, in case need to use in time calc
19784  int TrackSpeedCount = 0;
19785  float KmPerLocationStop;
19786  float MaxAllowableSpeed;
19787 
19788  //below added at v2.6.1
19789  if(TrainID > -1)
19790  {
19791  TTrain &Train = TrainVectorAtIdent(51, TrainID);
19792  Train.DistanceToStationStop = 0; //if find a red signal first then this distance isn't needed
19793  Train.StationStopCalculated = false;
19794  }
19795  AvTrackSpeed = 0;
19796  int CurrentElement = TrackVectorPosition;
19797  int CurrentEntryPos = TrackVectorPositionEntryPos;
19798  int NextElement;
19799  int NextEntryPos;
19800  int NextExitPos;
19801 
19802  CurrentStopTime = 0;
19803  LaterStopTime = 0;
19804  RecoverableTime = 0;
19805  if(CurrentElement == -1) // end element, no action needed
19806  {
19807  Utilities->CallLogPop(2094);
19808  return(-1);
19809  }
19810  int CurrentExitPos;
19811 
19812  // get ExitPos for first element to be measured
19813  if(Track->TrackElementAt(935, CurrentElement).TrackType == Points)
19814  {
19815  if((CurrentEntryPos == 0) || (CurrentEntryPos == 2)) // leading point
19816  {
19817  if(Track->TrackElementAt(936, CurrentElement).Attribute == 0)
19818  {
19819  CurrentExitPos = 1;
19820  }
19821  else
19822  {
19823  CurrentExitPos = 3;
19824  }
19825  }
19826  else
19827  {
19828  CurrentExitPos = 0; // trailing point
19829  }
19830  }
19831  else
19832  {
19833  CurrentExitPos = Track->GetNonPointsOppositeLinkPos(CurrentEntryPos);
19834  }
19835  // get CumTrackSpeed for first measured element
19836 
19837  TConfiguration CurrentExitConfig = Track->TrackElementAt(937, CurrentElement).Config[CurrentExitPos];
19838  int CurrentAttribute = Track->TrackElementAt(938, CurrentElement).Attribute;
19839 
19840  // check if currently stopped at a location, and if so add the remaining dwell time
19841  // can't use CurrentElement as that is in front of LeadElement and might not be at the location
19842  if(TrainID > -1)
19843  // -1 for a continuation and can't be at a location as not yet entered
19844  {
19845  TTrain &Train = TrainVectorAtIdent(39, TrainID); //Train wasn't a reference before v2.6.1 mods so FirstLaterStopRecoverableTime wouldn't be reset for the referenced train
19847  // this used to deduct from RecoverableTime when arrive at a location
19848  if(Train.StoppedAtLocation)
19849  {
19850  if(Train.StoppedForTrainInFront)
19851  {
19852  Utilities->CallLogPop(2082);
19853  return(-1); // no action needed
19854  }
19855  else if(!((Train.ActionVectorEntryPtr->FormatType == TimeTimeLoc) || (Train.ActionVectorEntryPtr->FormatType == TimeLoc)))
19856  {
19857  Utilities->CallLogPop(2083);
19858  return(-1); // not due a departure so no action needed
19859  }
19860  else // due a departure
19861  {
19862  double TimeToDepart = double(Train.ReleaseTime - TrainController->TTClockTime) * 86400 / 60; // mins to depart
19863  // can't convert a TDateTime to a float directly
19864  CurrentStopTime = float(TimeToDepart);
19865  AVPtr++;
19866  }
19867  }
19868  }
19869  // check if CurrentElement is a red signal, but ok if autosig route after
19870  if((CurrentExitConfig == Signal) && (CurrentAttribute == 0))
19871  // ok if autosig route after red signal
19872  {
19873  int NextElement = Track->TrackElementAt(939, CurrentElement).Conn[CurrentExitPos];
19874  int NextEntryPos = Track->TrackElementAt(940, CurrentElement).ConnLinkPos[CurrentExitPos];
19875  int RouteNumber; // holder for referenced value, not used
19876  if(AllRoutes->GetRouteTypeAndNumber(33, NextElement, NextEntryPos, RouteNumber) == TAllRoutes::AutoSigsRoute)
19877  {
19878  Utilities->CallLogPop(2078);
19879  return(-1);
19880  }
19881  else if(SigControlAndCanPassRedSignal)
19882  // ignore signal and increment CurrentElement to NextElement
19883  {
19884  if(Track->TrackElementAt(941, NextElement).TrackType == Points)
19885  {
19886  if((NextEntryPos == 0) || (NextEntryPos == 2))
19887  // leading entry point
19888  {
19889  if(Track->TrackElementAt(942, NextElement).Attribute == 0)
19890  {
19891  NextExitPos = 1;
19892  }
19893  else
19894  {
19895  NextExitPos = 3;
19896  }
19897  }
19898  else
19899  {
19900  NextExitPos = 0; // trailing entry point
19901  }
19902  }
19903  else
19904  {
19905  NextExitPos = Track->GetNonPointsOppositeLinkPos(NextEntryPos);
19906  }
19907  CurrentElement = NextElement;
19908  CurrentEntryPos = NextEntryPos;
19909  CurrentExitPos = NextExitPos;
19910  CurrentExitConfig = Track->TrackElementAt(943, CurrentElement).Config[CurrentExitPos];
19911  CurrentAttribute = Track->TrackElementAt(944, CurrentElement).Attribute;
19912  }
19913  else if((TrainID > -1) && (TrainVectorAtIdent(40, TrainID).TrainMode == Timetable)) // ignore signallercontrol or will
19914  // give 'NOW' indication after allowed to pass red signal when LeadMidLag (AllowedToPassRedSignal reset by this point)
19915  {
19916  Utilities->CallLogPop(2084);
19917  return(0);
19918  // stopped with red signal in front, don't need AvSpeedLimit in this case, & if at location awaiting departure dwell time already calculated
19919  }
19920  }
19921  int LaterStopNumber = 0;
19922  int x = 0;
19923  // added in v2.4.0 to prevent endless circling round track loops - spotted by Xeon 09/03/20 & reported by emsil
19924 
19925  while(!((CurrentExitConfig == Signal) && (CurrentAttribute == 0)))
19926  // not red signal next (in fwd direction) so enter loop to calc CumLength
19927  {
19928  x++; // added in v2.4.0 as above
19929  if(x > 5000)
19930  {
19931  Utilities->CallLogPop(2120);
19932  return(-1);
19933  }
19934  if(CurrentEntryPos > 1)
19935  {
19936  DistanceToRedSignal += Track->TrackElementAt(916, CurrentElement).Length23;
19937  CumTrackSpeed += Track->TrackElementAt(945, CurrentElement).SpeedLimit23;
19938  }
19939  else
19940  {
19941  DistanceToRedSignal += Track->TrackElementAt(917, CurrentElement).Length01;
19942  CumTrackSpeed += Track->TrackElementAt(946, CurrentElement).SpeedLimit01;
19943  }
19944  TrackSpeedCount++;
19945 
19946  //added for multiplayer - exiting at a continuation and continuation length already added
19947  if((Track->TrackElementAt(1407, CurrentElement).TrackType == Continuation) && (Track->TrackElementAt(1408, CurrentElement).Config[CurrentExitPos] == End))
19948  {
19949  DistanceToExit = DistanceToRedSignal; //don't need to exit function here as will exit when find that the next Conn value is -1
19950  ExitPair.first = Track->TrackElementAt(1409, CurrentElement).HLoc;
19951  ExitPair.second = Track->TrackElementAt(1410, CurrentElement).VLoc;
19952  //here repeat calcs for MaxAllowableSpeed & AvTrackSpeed as done at end for stop signal
19953  //need here as next element will be -1 so will exit before calcs at end
19954  if(TrackSpeedCount > 0)
19955  {
19956  MaxAllowableSpeed = CumTrackSpeed / TrackSpeedCount;
19957  }
19958  else // shouldn't reach here but include to prevent divide by zero error
19959  {
19960  if(CurrentEntryPos > 1)
19961  {
19962  MaxAllowableSpeed = Track->TrackElementAt(951, CurrentElement).SpeedLimit23;
19963  }
19964  else
19965  {
19966  MaxAllowableSpeed = Track->TrackElementAt(952, CurrentElement).SpeedLimit01;
19967  }
19968  // Train MaxRunningSpeed taken into account in RebuildOpTimeToActMultimap
19969  }
19970  //calc AvTrackSpeed
19971  if(LaterStopNumber > 0)
19972  {
19973  KmPerLocationStop = float(DistanceToRedSignal) / LaterStopNumber / 1000; // m to km
19974  AvTrackSpeed = (8.75 * KmPerLocationStop) + 44;
19975  // Av speed calculation based on formula: Speed = 8.75*(kms/location stop) + 44 km/sec (from experiments), subject to a maximum of
19976  // average line speed/2 (for half distance accelerating and half decelerating.
19977  }
19978  else
19979  {
19980  AvTrackSpeed = (sqrt(float(DistanceToRedSignal) / 1000) * 44) + 60;
19981  // using linear trendline for accel & decel distance at various speeds
19982  // at half braking, speed never < 60 using this
19983  }
19984  if(AvTrackSpeed > MaxAllowableSpeed)
19985  {
19986  AvTrackSpeed = MaxAllowableSpeed;
19987  }
19988  }
19989 
19990  // added at v2.6.1 to find DistanceToStationStop for trains running early
19991  if(TrainID > -1) //can ignore continuation entries as these don't run early
19992  {
19993  TTrain &Train = TrainVectorAtIdent(52, TrainID);
19994  if(!Train.StationStopCalculated)
19995  {
19996  if(Train.TrainMode == Timetable)
19997  {
19998  bool StopRequired = false;
19999  if(!Train.TimetableFinished && (Train.NameInTimetableBeforeCDT(16, Track->TrackElementAt(1005, CurrentElement).ActiveTrackElementName,
20000  StopRequired) > -1) && ((Track->TrackElementAt(1006, CurrentElement).StationEntryStopLinkPos1 == CurrentEntryPos) ||
20001  (Track->TrackElementAt(1010, CurrentElement).StationEntryStopLinkPos2 == CurrentEntryPos)))
20002  {
20003  // no need to add in the length of element to CumulativeLength
20004  if(StopRequired)
20005  {
20006  Train.DistanceToStationStop = DistanceToRedSignal; // DistanceToRedSignal holds the intermediate distance to this point
20007  Train.StationStopCalculated = true; //don't want to update it with later stops
20008  }
20009  }
20010  }
20011  }
20012  }
20013  // check for train in front, but if on a bridge on other track then ok
20014  TTrackElement TE = Track->TrackElementAt(947, CurrentElement);
20015  int TrainOnElement;
20016  if(TE.TrackType != Bridge)
20017  {
20018  TrainOnElement = TE.TrainIDOnElement;
20019  }
20020  else
20021  {
20022  if(CurrentEntryPos > 1)
20023  {
20024  TrainOnElement = TE.TrainIDOnBridgeTrackPos23;
20025  }
20026  else
20027  {
20028  TrainOnElement = TE.TrainIDOnBridgeTrackPos01;
20029  }
20030  }
20031  if((TrainOnElement > -1) && (TrainOnElement != TrainID))
20032  // train in front before red signal
20033  {
20034  Utilities->CallLogPop(2085);
20035  return(-1);
20036  }
20037  // add to stoptime if required
20038  if(Track->TrackElementAt(948, CurrentElement).ActiveTrackElementName != "")
20039  {
20040  double StopTimeDouble;
20041  while(AVPtr->FormatType == PassTime)
20042  {
20043  AVPtr++; // skip past any passes
20044  }
20045  if((Track->TrackElementAt(949, CurrentElement).ActiveTrackElementName == AVPtr->LocationName) && ((AVPtr->FormatType == TimeLoc) ||
20046  (AVPtr->FormatType == TimeTimeLoc)))
20047  // stop due here so calc dwell time & advance Ptr
20048  {
20049  if(AVPtr->FormatType == TimeTimeLoc)
20050  {
20051  LaterStopNumber++;
20052  StopTimeDouble = double(AVPtr->DepartureTime - AVPtr->ArrivalTime) * 86400.0 / 60.0;
20053  if(StopTimeDouble < 0.5)
20054  {
20055  StopTimeDouble = 0.5;
20056  }
20057  // at least 30 secs delay at station
20058  // can't convert a TDateTime to a float directly
20059  LaterStopTime += float(StopTimeDouble);
20060  RecoverableTime += StopTimeDouble - 0.5;
20061  if((LaterStopNumber == 1) && (TrainID > -1))
20062  {
20063  TrainVectorAtIdent(41, TrainID).FirstLaterStopRecoverableTime = RecoverableTime;
20064  }
20065  AVPtr++;
20066  }
20067  else if((AVPtr->FormatType == TimeLoc) && (AVPtr->ArrivalTime != TDateTime(-1))) // must be an arrival
20068  {
20069  if((AVPtr + 1)->FormatType == TimeLoc)
20070  // must be a departure
20071  {
20072  LaterStopNumber++;
20073  StopTimeDouble = double((AVPtr + 1)->DepartureTime - AVPtr->ArrivalTime) * 86400.0 / 60.0;
20074  // can't convert a TDateTime to a float directly
20075  if(StopTimeDouble < 0.5)
20076  {
20077  StopTimeDouble = 0.5;
20078  }
20079  // at least 30 secs delay at station
20080  LaterStopTime += float(StopTimeDouble);
20081  RecoverableTime += StopTimeDouble - 0.5;
20082  if((LaterStopNumber == 1) && (TrainID > -1))
20083  {
20084  TrainVectorAtIdent(42, TrainID).FirstLaterStopRecoverableTime = RecoverableTime;
20085  }
20086  AVPtr++;
20087  AVPtr++;
20088  }
20089  else // not a departure, does something else at the location so no calculation needed
20090  {
20091  Utilities->CallLogPop(2086);
20092  return(-1);
20093  }
20094  }
20095  }
20096  }
20097  NextElement = Track->TrackElementAt(950, CurrentElement).Conn[CurrentExitPos];
20098  if(NextElement == -1) // reached end element, no action needed
20099  {
20100  Utilities->CallLogPop(2077);
20101  return(-1);
20102  }
20103  NextEntryPos = Track->TrackElementAt(919, CurrentElement).ConnLinkPos[CurrentExitPos];
20104  // get NextExitPos
20105  if(Track->TrackElementAt(920, NextElement).TrackType == Points)
20106  {
20107  if((NextEntryPos == 0) || (NextEntryPos == 2))
20108  // leading entry point
20109  {
20110  if(Track->TrackElementAt(921, NextElement).Attribute == 0)
20111  {
20112  NextExitPos = 1;
20113  }
20114  else
20115  {
20116  NextExitPos = 3;
20117  }
20118  }
20119  else
20120  {
20121  NextExitPos = 0; // trailing entry point
20122  }
20123  }
20124  else
20125  {
20126  NextExitPos = Track->GetNonPointsOppositeLinkPos(NextEntryPos);
20127  }
20128  CurrentElement = NextElement;
20129  CurrentEntryPos = NextEntryPos;
20130  CurrentExitPos = NextExitPos;
20131  CurrentExitConfig = Track->TrackElementAt(922, CurrentElement).Config[CurrentExitPos];
20132  CurrentAttribute = Track->TrackElementAt(923, CurrentElement).Attribute;
20133  }
20134  if((CurrentExitConfig == Signal) && (CurrentAttribute == 0))
20135  // ok if autosig route after red signal, no action needed
20136  {
20137  int NextElement = Track->TrackElementAt(924, CurrentElement).Conn[CurrentExitPos];
20138  int NextEntryPos = Track->TrackElementAt(925, CurrentElement).ConnLinkPos[CurrentExitPos];
20139  int RouteNumber; // holder for referenced value, not used
20140  if(AllRoutes->GetRouteTypeAndNumber(31, NextElement, NextEntryPos, RouteNumber) == TAllRoutes::AutoSigsRoute)
20141  {
20142  Utilities->CallLogPop(2095);
20143  return(-1);
20144  }
20145  }
20146 
20147  if(TrackSpeedCount > 0)
20148  {
20149  MaxAllowableSpeed = CumTrackSpeed / TrackSpeedCount;
20150  }
20151  else // shouldn't reach here but include to prevent divide by zero error
20152  {
20153  if(CurrentEntryPos > 1)
20154  {
20155  MaxAllowableSpeed = Track->TrackElementAt(1433, CurrentElement).SpeedLimit23;
20156  }
20157  else
20158  {
20159  MaxAllowableSpeed = Track->TrackElementAt(1434, CurrentElement).SpeedLimit01;
20160  }
20161  // Train MaxRunningSpeed taken into account in RebuildOpTimeToActMultimap
20162  }
20163 
20164  if(LaterStopNumber > 0)
20165  {
20166  KmPerLocationStop = float(DistanceToRedSignal) / LaterStopNumber / 1000; // m to km
20167  AvTrackSpeed = (8.75 * KmPerLocationStop) + 44;
20168  }
20169  else
20170  // Av speed calculation based on formula: Speed = 8.75*(kms/location stop) + 44 km/sec (from experiments), subject to a maximum of
20171  // average line speed/2 (for half distance accelerating and half decelerating.
20172  {
20173  AvTrackSpeed = (sqrt(float(DistanceToRedSignal) / 1000) * 44) + 60;
20174  // using linear trendline for accel & decel distance at various speeds
20175  // at half braking, speed never < 60 using this
20176  }
20177  if(AvTrackSpeed > MaxAllowableSpeed)
20178  {
20179  AvTrackSpeed = MaxAllowableSpeed;
20180  }
20181  Utilities->CallLogPop(2096);
20182  return(DistanceToRedSignal);
20183 }
20184 
20185 // ---------------------------------------------------------------------------
20186 // end of TTrainController entries
20187 // ---------------------------------------------------------------------------
TTrain::LinkOccupied
bool LinkOccupied(int Caller, int TrackVectorPosition, int LinkNumber)
Added at v1.2.0: true if any part of train on specific link, false otherwise, including no link prese...
Definition: TrainUnit.cpp:8437
TAllRoutes::TrackIsInARoute
bool TrackIsInARoute(int Caller, int TrackVectorPosition, int LinkPos)
Examines Route2MultiMap and if the element at TrackVectorPosition with LinkPos (can be entry or exit)...
Definition: TrackUnit.cpp:17594
TActionVectorEntry::EventTime
TDateTime EventTime
Definition: TrainUnit.h:134
TTrain::AllowedToPassRedSignal
bool AllowedToPassRedSignal
set when train has been called on, or when under signaller control and instructed to pass a red signa...
Definition: TrainUnit.h:375
JoinedByOther
@ JoinedByOther
Definition: TrainUnit.h:50
TTrainController::CheckShuttleRepeatTime
bool CheckShuttleRepeatTime(int Caller, TDateTime ForwardEventTime, TDateTime ReverseEventTime, int RepeatMinutes)
Check that shuttle link services have consistent times, true for success.
Definition: TrainUnit.cpp:14370
TTrain::ZeroPowerNoNewShuttleFromNonRepeatMessage
bool ZeroPowerNoNewShuttleFromNonRepeatMessage
Definition: TrainUnit.h:341
TTrain::ZeroPowerNoNewServiceMessage
bool ZeroPowerNoNewServiceMessage
Definition: TrainUnit.h:340
TTrain::VOffset
int VOffset[4]
each headcode character is an 8x8 pixel graphic and must be placed within a 16x16 pixel element,...
Definition: TrainUnit.h:475
TTrainController
Handles all train and timetable activities, only one object created.
Definition: TrainUnit.h:681
TTrain::TTrain
TTrain(int Caller, int RearStartElementIn, int RearStartExitPosIn, AnsiString InputCode, int StartSpeed, int Mass, double MaxRunningSpeed, double MaxBrakeRate, double PowerAtRail, TTrainMode TrainMode, TTrainDataEntry *TrainDataEntryPtr, int RepeatNumber, int IncrementalMinutes, int IncrementalDigits, int SignallerMaxSpeed)
Constructor, sets listed member values.
Definition: TrainUnit.cpp:71
TAllRoutes::LockedRouteVector
TLockedRouteVector LockedRouteVector
the vector that stores all the locked routes on the railway
Definition: TrackUnit.h:1690
TRailGraphics::smOrange
Graphics::TBitmap * smOrange
Definition: GraphicUnit.h:899
TActionVector
std::vector< TActionVectorEntry > TActionVector
contains all actions for a single train
Definition: TrainUnit.h:172
TUtilities::LoadFileString
AnsiString LoadFileString(std::ifstream &InFile)
loads a string value from the file
Definition: Utilities.cpp:190
TTrainController::CheckNonRepeatingShuttleLinksAndSetData
bool CheckNonRepeatingShuttleLinksAndSetData(int Caller, AnsiString MainHeadCode, AnsiString NonRepeatingHeadCode, bool GiveMessages)
A timetable validation function where cross references are checked for validity for non-repeating shu...
Definition: TrainUnit.cpp:14393
TTrainController::CheckStartPositionValidity
bool CheckStartPositionValidity(int Caller, AnsiString RearElementStr, AnsiString FrontElementStr, bool GiveMessages)
A timetable validation function where train starting positions are checked for validity,...
Definition: TrainUnit.cpp:14038
TTrain::MidEntryPos
int MidEntryPos
Definition: TrainUnit.h:362
TTrainController::RebuildOpTimeToActMultimap
void RebuildOpTimeToActMultimap(int Caller)
new v2.2.0 for OperatorActionPanel
Definition: TrainUnit.cpp:19570
TTrainController::PwrHigh
bool PwrHigh
Definition: TrainUnit.h:789
TTrainController::BuildContinuationTrainExpectationMultiMap
void BuildContinuationTrainExpectationMultiMap(int Caller)
populate the ContinuationTrainExpectationMultiMap during timetable loading
Definition: TrainUnit.cpp:15435
TTrain::CheckNewServiceDepartureTime
AnsiString CheckNewServiceDepartureTime(int Caller, TActionVectorEntry *Ptr, int RptNum, TTrainDataEntry *LinkedTrainDataPtr, AnsiString RetStr)
called during FloatingLabelNextString to find the next service departure time
Definition: TrainUnit.cpp:7040
TFixedTrackPiece::GraphicPtr
Graphics::TBitmap * GraphicPtr
the track bitmap for display on the zoomed-in railway
Definition: TrackUnit.h:91
SignallerMoveForwards
@ SignallerMoveForwards
Definition: TrainUnit.h:51
TRailGraphics::CodeR
Graphics::TBitmap * CodeR
Definition: GraphicUnit.h:995
Create
@ Create
Definition: TrainUnit.h:50
TTrainController::TContinuationEntryVecPosVector
std::vector< int > TContinuationEntryVecPosVector
ensures only one train displayed for a given continuation
Definition: TrainUnit.h:768
TAllRoutes::SetAllRearwardsSignals
void SetAllRearwardsSignals(int Caller, int Attribute, int RouteNumber, int RouteStartPosition)
Set rearwards signals from the specified route starting position.
Definition: TrackUnit.cpp:18600
Arrive
@ Arrive
Definition: TrainUnit.h:50
ChangeDirection
@ ChangeDirection
Definition: TrainUnit.h:50
TTrain::ZeroPowerNoCDTMessage
bool ZeroPowerNoCDTMessage
Definition: TrainUnit.h:339
TTrain::CallingOnFlag
bool CallingOnFlag
calling on permitted
Definition: TrainUnit.h:381
Depart
@ Depart
Definition: TrainUnit.h:50
TRailGraphics::gl89set
Graphics::TBitmap * gl89set
Definition: GraphicUnit.h:716
TTrainController::CheckHeadCodeValidity
bool CheckHeadCodeValidity(int Caller, bool GiveMessages, AnsiString HeadCode)
Returns true if the headcode complies with requirements.
Definition: TrainUnit.cpp:11241
TRailGraphics::gl88set
Graphics::TBitmap * gl88set
Definition: GraphicUnit.h:714
clBufferStopBackground
#define clBufferStopBackground
Definition: GraphicUnit.h:291
TTrain::MaxRunningSpeed
double MaxRunningSpeed
the current maximum train running speed
Definition: TrainUnit.h:421
TTrack::BarriersDownVector
TActiveLCVector BarriersDownVector
vector of LCs with barriers down
Definition: TrackUnit.h:780
TPrefDirElement::GetXLinkPos
int GetXLinkPos() const
Returns the XLink array position.
Definition: TrackUnit.h:281
TTrack::IsLCBarrierDownAtHV
bool IsLCBarrierDownAtHV(int Caller, int HLoc, int VLoc)
True if an open (to trains) level crossing is found at H & V.
Definition: TrackUnit.cpp:7181
TAllRoutes::TLockedRouteClass::TruncateTrackVectorPosition
unsigned int TruncateTrackVectorPosition
the TrackVector position of the element selected for truncation
Definition: TrackUnit.h:1607
RestoreTimetableControl
@ RestoreTimetableControl
Definition: TrainUnit.h:51
FailCrashed
@ FailCrashed
Definition: TrainUnit.h:41
TRailGraphics::TempHeadCode
Graphics::TBitmap * TempHeadCode
Definition: GraphicUnit.h:905
TAllRoutes::AutoSigsRoute
@ AutoSigsRoute
Definition: TrackUnit.h:1619
TTrack::TSigElement::Attribute
int Attribute
the signal state - red, yellow, double yellow or green
Definition: TrackUnit.h:711
TActionVectorEntry::LocationType
TTimetableLocationType LocationType
indicates where the train is when the relevant action occurs
Definition: TrainUnit.h:140
TTrack::GapFlashGreenPosition
int GapFlashGreenPosition
Definition: TrackUnit.h:764
TAllRoutes::TRouteElementPair
std::pair< int, unsigned int > TRouteElementPair
defines a specific element in a route, the first (int) value is the vector position in the AllRoutesV...
Definition: TrackUnit.h:1631
NamedNonStationLocation
@ NamedNonStationLocation
Definition: TrackUnit.h:66
FinRemHere
@ FinRemHere
Definition: TrainUnit.h:64
TTrain::HeadCodeGrPtr
Graphics::TBitmap * HeadCodeGrPtr[4]
points to the headcode segment graphics e.g. 5,A,4,7.
Definition: TrainUnit.h:492
TTrainController::TContinuationTrainExpectationEntry::TrainDataEntryPtr
TTrainDataEntry * TrainDataEntryPtr
points to the service entry in the timetable's TrainDataVector
Definition: TrainUnit.h:729
TTrain::ChangeTrainDirection
void ChangeTrainDirection(int Caller)
Reverses the direction of motion of the train.
Definition: TrainUnit.cpp:6198
FSHNewService
@ FSHNewService
Definition: TrainUnit.h:65
TTrain::MaxBrakeRate
double MaxBrakeRate
the maximum brake rate that the train can achieve
Definition: TrainUnit.h:425
TTimetableLocationType
TTimetableLocationType
Definition: TrainUnit.h:69
TAllRoutes::DiagonalFouledByRoute
bool DiagonalFouledByRoute(int Caller, int HLoc, int VLoc, int DiagonalLinkNumber)
As above but only checks for a route (may or may not be a train present (new at v1....
Definition: TrackUnit.cpp:19349
TTrainController::CheckForDuplicateCrossReferences
bool CheckForDuplicateCrossReferences(int Caller, AnsiString MainHeadCode, AnsiString SecondHeadCode, bool GiveMessages)
A timetable validation function where referenced services are checked for uniqueness,...
Definition: TrainUnit.cpp:13316
TTrackElement::StationEntryStopLinkPos2
int StationEntryStopLinkPos2
Used for track at platforms and non-station named locations to mark the train front element stop posi...
Definition: TrackUnit.h:150
TTrain::MidExitPos
int MidExitPos
Definition: TrainUnit.h:362
TRailGraphics::CodeD
Graphics::TBitmap * CodeD
Definition: GraphicUnit.h:981
TTrain::TrainFailed
bool TrainFailed
added at v2.4.0 to indicate failure
Definition: TrainUnit.h:407
TUtilities::CheckFileStringZeroDelimiter
bool CheckFileStringZeroDelimiter(std::ifstream &InFile)
checks that the value is a string ('0' only accepted as the delimiter), returns true for success
Definition: Utilities.cpp:435
DisplayUnit.h
TRailGraphics::Code_w
Graphics::TBitmap * Code_w
Definition: GraphicUnit.h:964
FailDerailed
@ FailDerailed
Definition: TrainUnit.h:41
TTrainController::SPADWarning
bool SPADWarning
Definition: TrainUnit.h:775
TTrain::ZeroPowerNoFrontSplitMessage
bool ZeroPowerNoFrontSplitMessage
Definition: TrainUnit.h:335
TRailGraphics::Code_s
Graphics::TBitmap * Code_s
Definition: GraphicUnit.h:960
TTrainController::GetExitLocationAndAt
AnsiString GetExitLocationAndAt(int Caller, TNumList &ExitList, AnsiString &AllowedExits) const
Check all timetable names in ExitList, if all same return " at [name]" + AllowableExits = elements,...
Definition: TrainUnit.cpp:18881
TTrain::SaveOneSessionTrain
void SaveOneSessionTrain(int Caller, std::ofstream &OutFile)
Data for a single train is saved to a session file.
Definition: TrainUnit.cpp:7313
TTrain::DepartureTimeSet
bool DepartureTimeSet
set when stopped at a location and the next action is departure (set in UpdateTrain when ReleaseTime ...
Definition: TrainUnit.h:383
TTrain::Plotted
bool Plotted
set when train plotted on screen
Definition: TrainUnit.h:466
TTrain::LagElement
int LagElement
Definition: TrainUnit.h:362
TTrain::RemainHere
void RemainHere(int Caller)
Sends the 'train terminated' message to the performance log and sets TimetableFinished to true.
Definition: TrainUnit.cpp:6312
TTrainController::BufferAttentionWarning
bool BufferAttentionWarning
Definition: TrainUnit.h:775
TTrainController::TrainVectorAtIdent
TTrain & TrainVectorAtIdent(int Caller, int TrainID)
Return a reference to the train with ID TrainID, carries out validity checking on TrainID.
Definition: TrainUnit.cpp:9641
TPrefDirElement::GetRouteEXGraphicPtr
Graphics::TBitmap * GetRouteEXGraphicPtr()
Returns route graphic.
Definition: TrackUnit.h:317
THVShortPair
std::pair< short, short > THVShortPair
Definition: InterfaceUnit.h:81
TRailGraphics::smLightBlue
Graphics::TBitmap * smLightBlue
Definition: GraphicUnit.h:896
TTrainController::TContinuationAutoSigEntry::RouteNumber
int RouteNumber
the AllRoutesVector position of the route that the continuation is in
Definition: TrainUnit.h:703
TTrainController::StopTTClockMessage
void StopTTClockMessage(int Caller, AnsiString Message)
sends a message to the user and stops the timetable clock while it is displayed
Definition: TrainUnit.cpp:15166
TExitInfo::TimeToExitSecs
short TimeToExitSecs
Definition: TrainUnit.h:108
TTrainController::OperatingTrainLateArr
int OperatingTrainLateArr
< all these set to 0 in constructor
Definition: TrainUnit.h:834
TRailGraphics::CodeJ
Graphics::TBitmap * CodeJ
Definition: GraphicUnit.h:987
TUtilities::IncrementAnsiTimeOneMinute
AnsiString IncrementAnsiTimeOneMinute(AnsiString TimeVal)
takes "HH:MM" and increments it to "HH:MX", where MX == MM + 1, incrementing the hour if necessary
Definition: Utilities.cpp:826
TRailGraphics::CodeQ
Graphics::TBitmap * CodeQ
Definition: GraphicUnit.h:994
TDisplay::GetOutputLog9
TLabel * GetOutputLog9()
Definition: DisplayUnit.h:185
TAllRoutes::RemoveRouteElement
void RemoveRouteElement(int Caller, int HLoc, int VLoc, int ELink)
Erases the route element from Route2MultiMap and from the PrefDirVector.
Definition: TrackUnit.cpp:18390
TTrainController::CrashWarning
bool CrashWarning
Definition: TrainUnit.h:775
TTrainController::CheckLocationValidity
bool CheckLocationValidity(int Caller, AnsiString LocStr, bool GiveMessages, bool CheckLocationsExistInRailway)
Returns true if the location name complies with requirements.
Definition: TrainUnit.cpp:11194
TAllRoutes::TLockedRouteClass::LastXLinkPos
int LastXLinkPos
the XLinkPos value of the last (i.e. most forward) element in the route
Definition: TrackUnit.h:1611
TTrain::PlotTrain
void PlotTrain(int Caller, TDisplay *Disp)
Plots the train on the display in normal (zoomed-in) mode.
Definition: TrainUnit.cpp:8409
TNumListIterator
TNumList::iterator TNumListIterator
Definition: TrainUnit.h:95
FailLevelCrossingCrash
@ FailLevelCrossingCrash
Definition: TrainUnit.h:43
TActionVectorEntry::DepartureTime
TDateTime DepartureTime
relevant times at which the action is timetabled, zeroed on creation so change to -1 as a marker for ...
Definition: TrainUnit.h:134
TRailGraphics::Code_f
Graphics::TBitmap * Code_f
Definition: GraphicUnit.h:947
FailCreateLockedRoute
@ FailCreateLockedRoute
Definition: TrainUnit.h:42
TTrainController::TContinuationTrainExpectationEntry::IncrementalDigits
int IncrementalDigits
Repeat headcode separation.
Definition: TrainUnit.h:725
TTrainController::SaveSessionTrains
void SaveSessionTrains(int Caller, std::ofstream &SessionFile)
save trains to a session file
Definition: TrainUnit.cpp:15181
TOneCompleteFormattedTrain::HeadCode
AnsiString HeadCode
Definition: TrainUnit.h:263
Intermediate
@ Intermediate
Definition: TrainUnit.h:75
TRailGraphics::Code_d
Graphics::TBitmap * Code_d
Definition: GraphicUnit.h:945
TTrain::JoinedBy
void JoinedBy(int Caller)
Carry out the actions needed when a train is waiting to be joined by another train.
Definition: TrainUnit.cpp:6127
FailIncorrectExit
@ FailIncorrectExit
Definition: TrainUnit.h:43
TRailGraphics::Code_t
Graphics::TBitmap * Code_t
Definition: GraphicUnit.h:961
TRailGraphics::ChangeBackgroundColour
void ChangeBackgroundColour(int Caller, Graphics::TBitmap *BitmapIn, Graphics::TBitmap *BitmapOut, TColor NewBackgroundColour, TColor OldBackgroundColour, bool &ColourError)
Definition: GraphicUnit.cpp:3499
TTrain::GetLeadElement
int GetLeadElement()
get LeadElement - used in RouteLockingRequired in TrackUnit.cpp
Definition: TrainUnit.h:663
TimeCmd
@ TimeCmd
Definition: TrainUnit.h:64
TTrain::BackgroundPtr
Graphics::TBitmap * BackgroundPtr[4]
the existing track graphic that the train headcode segment covers up (one for each headcode segment)
Definition: TrainUnit.h:488
TUtilities::LoadFileDouble
double LoadFileDouble(std::ifstream &InFile)
loads a double value from the file (converts from a string to a double) and uses the local decimal po...
Definition: Utilities.cpp:172
TTrain::DistanceToStationStop
int DistanceToStationStop
calculated in UpdateTrain & used in CalcDistanceToRedSignalandStopTime to cater for trains running ea...
Definition: TrainUnit.h:448
TTrainController::EntryPos
int EntryPos(int Caller, int TrainIDIn, int TrackVectorNumber)
Return the track entry link (Link[]) array position for the given train on track element at track vec...
Definition: TrainUnit.cpp:9603
SignallerLeave
@ SignallerLeave
Definition: TrainUnit.h:52
TTrain::PlotEntryPos
int PlotEntryPos[4]
the LinkPos value corresponding to the train entry link of the element where each of the 4 headcode c...
Definition: TrainUnit.h:481
NotStarted
@ NotStarted
Definition: TrainUnit.h:86
TTrack::OneNamedLocationElementAtLocation
bool OneNamedLocationElementAtLocation(int Caller, AnsiString LocationName)
True if there is at least one named location element with name 'LocationName', used in timetable inte...
Definition: TrackUnit.cpp:10749
TTrainController::SPADEvents
int SPADEvents
Definition: TrainUnit.h:828
TTrainController::LastTTTime
AnsiString LastTTTime
Stores the last time used in the timetable as an AnsiString - used for timetable analysis.
Definition: TrainUnit.h:771
TTrain::ZeroPowerNoRepeatShuttleMessage
bool ZeroPowerNoRepeatShuttleMessage
Definition: TrainUnit.h:342
TTrackElement::SigAspect
enum TTrackElement::@1 SigAspect
TRailGraphics::gl90set
Graphics::TBitmap * gl90set
Definition: GraphicUnit.h:719
TTrain::ActionVectorEntryPtr
TActionVectorEntry * ActionVectorEntryPtr
points to the current position in the ActionVector (a member of the TTrainDataEntry class)
Definition: TrainUnit.h:369
TTrainDataEntry
Contains all data for a single timetable service entry.
Definition: TrainUnit.h:205
LeadMid
@ LeadMid
Definition: TrainUnit.h:296
FailMissedPass
@ FailMissedPass
Definition: TrainUnit.h:42
clSignalStopBackground
#define clSignalStopBackground
Definition: GraphicUnit.h:299
TTrainController::LateDeps
int LateDeps
Definition: TrainUnit.h:818
TAllRoutes::IsElementInLockedRouteGetPrefDirElementGetLockedVectorNumber
bool IsElementInLockedRouteGetPrefDirElementGetLockedVectorNumber(int Caller, int TrackVectorPosition, int XLinkPos, TPrefDirElement &PrefDirElement, int &LockedVectorNumber)
Checks whether the preferred direction element at TrackVectorPosition with XLinkPos value is in a loc...
Definition: TrackUnit.cpp:18864
TrackUnit.h
TTrainController::CheckStartAllowable
bool CheckStartAllowable(int Caller, int RearPosition, int RearExitPos, AnsiString HeadCode, bool ReportFlag, TActionEventType &EventType)
Called when trying to introduce a new train - checks for points in correct orientation,...
Definition: TrainUnit.cpp:14118
TTrainController::RebuildTimeToExitMultiMap
void RebuildTimeToExitMultiMap(int Caller)
new for multiplayer
Definition: TrainUnit.cpp:19732
TRailGraphics::Code_r
Graphics::TBitmap * Code_r
Definition: GraphicUnit.h:959
TTrain::SendMissedActionLogs
void SendMissedActionLogs(int Caller, int IncNum, TActionVectorEntry *Ptr)
Missed actions (see NameInTimetableBeforeCDT above) sent to the performance log and performance file.
Definition: TrainUnit.cpp:6333
TTrainController::TContinuationTrainExpectationEntry::RepeatNumber
int RepeatNumber
service RepeatNumber
Definition: TrainUnit.h:721
TRailGraphics::CodeX
Graphics::TBitmap * CodeX
Definition: GraphicUnit.h:1001
TOneTrainFormattedEntry::Time
AnsiString Time
the time of the action as a string
Definition: TrainUnit.h:250
TRailGraphics::CodeT
Graphics::TBitmap * CodeT
Definition: GraphicUnit.h:997
TTrainDataEntry::PowerAtRail
double PowerAtRail
in Watts (taken as 80% of the train's Gross Power, i.e. that entered by the user)
Definition: TrainUnit.h:213
TTrainController::LastTrainLoaded
int LastTrainLoaded
displays last train loaded from session file, used for debugging
Definition: TrainUnit.h:838
TTrainController::NotStartedTrainLateMins
float NotStartedTrainLateMins
total late minutes of trains that haven't started yet on exit operation for locations not reached yet
Definition: TrainUnit.h:795
Utilities.h
TTrainController::TContinuationAutoSigVectorIterator
TContinuationAutoSigVector::iterator TContinuationAutoSigVectorIterator
Definition: TrainUnit.h:711
TTrain::SkippedDeparture
bool SkippedDeparture
used to indicate that a departure is still awaited when later timetabled events are to be skipped
Definition: TrainUnit.h:323
TTrain::TrainOnContinuation
bool TrainOnContinuation(int Caller)
Returns true if any part of train on a continuation - called when checking for failures,...
Definition: TrainUnit.cpp:8844
TTrainController::CheckSessionLockedRoutes
bool CheckSessionLockedRoutes(int Caller, std::ifstream &SessionFile)
Part of the session file integrity check for locked routes, true for success.
Definition: TrainUnit.cpp:15284
FailTrainEntry
@ FailTrainEntry
Definition: TrainUnit.h:40
MidLag
@ MidLag
Definition: TrainUnit.h:296
TDisplay::Update
void Update()
Repaint the screen display.
Definition: DisplayUnit.h:222
TTrainController::LocServiceTimesDepTimeSort
bool LocServiceTimesDepTimeSort(TLocServiceTimes i, TLocServiceTimes j)
Definition: TrainUnit.h:875
TTrainController::CheckSessionTrains
bool CheckSessionTrains(int Caller, std::ifstream &InFile)
Part of the session file integrity check for train entries, true for success.
Definition: TrainUnit.cpp:15222
StartNew
@ StartNew
Definition: TrainUnit.h:64
TakeSignallerControl
@ TakeSignallerControl
Definition: TrainUnit.h:50
LeadMidLag
@ LeadMidLag
Definition: TrainUnit.h:296
TTrack::GapFlashGreen
TGraphicElement * GapFlashGreen
Definition: TrackUnit.h:784
TTrain::ZeroPowerNoRepeatShuttleOrNewServiceMessage
bool ZeroPowerNoRepeatShuttleOrNewServiceMessage
flags to indicate whether the respective message has been sent
Definition: TrainUnit.h:343
TrainFailure
@ TrainFailure
Definition: TrainUnit.h:51
TTrain::MinsDelayed
float MinsDelayed
new at v2.2.0 for operator time to act panel. Calculated in UpdateTrain
Definition: TrainUnit.h:436
TTrack::TSigElement::SigPtr
Graphics::TBitmap * SigPtr
pointer to the graphic
Definition: TrackUnit.h:713
TTrainController::TimeToExitMultiMap
TTimeToExitMultiMap TimeToExitMultiMap
Map of times to exit & exit coordinates.
Definition: TrainUnit.h:978
TAllRoutes::FindRouteNumberFromRoute2MultiMapNoErrors
bool FindRouteNumberFromRoute2MultiMapNoErrors(int Caller, int HLoc, int VLoc, int ELink, int &RouteNumber)
If a route is present at H, V & Elink returns true with RouteNumber giving vector position in AllRout...
Definition: TrackUnit.cpp:18138
TTrainController::Last2CharactersBothDigits
bool Last2CharactersBothDigits(int Caller, AnsiString HeadCode)
Checks the last two characters in HeadCode and returns true if both are digits.
Definition: TrainUnit.cpp:10802
TRailGraphics::Code2
Graphics::TBitmap * Code2
Definition: GraphicUnit.h:970
TTrack::GetVectorPositionsFromInactiveTrackMap
TIMPair GetVectorPositionsFromInactiveTrackMap(int Caller, int HLoc, int VLoc, bool &FoundFlag)
Similar to GetVectorPositionFromTrackMap but for inactive elements, a pair is returned because there ...
Definition: TrackUnit.cpp:5773
TTrain::BackgroundColour
TColor BackgroundColour
the background colour of the train's headcode graphics
Definition: TrainUnit.h:495
TTrainOperatingData::EventReported
TActionEventType EventReported
Definition: TrainUnit.h:184
TTrain
Definition: TrainUnit.h:302
TRailGraphics::Code_z
Graphics::TBitmap * Code_z
Definition: GraphicUnit.h:967
TTrainController::CheckTimeValidity
bool CheckTimeValidity(int Caller, AnsiString TimeStr, TDateTime &Time)
returns true if the time complies with requirements
Definition: TrainUnit.cpp:10821
clSignallerStopped
#define clSignallerStopped
Definition: GraphicUnit.h:298
TTrackElement::TrainIDOnBridgeTrackPos23
int TrainIDOnBridgeTrackPos23
Set to the TrainID value when a train is present on the element, bridges can have two trains present ...
Definition: TrackUnit.h:152
TOnePrefDir::GetFixedPrefDirElementAt
const TPrefDirElement & GetFixedPrefDirElementAt(int Caller, int At) const
Return a non-modifiable element at PrefDirVector position 'At'.
Definition: TrackUnit.cpp:11379
FailBuffersPreventingStart
@ FailBuffersPreventingStart
Definition: TrainUnit.h:43
TTrainController::LocServiceTimesArrTimeSort
bool LocServiceTimesArrTimeSort(TLocServiceTimes i, TLocServiceTimes j)
Definition: TrainUnit.h:870
TOneTrainFormattedEntry::Action
AnsiString Action
includes location if relevant
Definition: TrainUnit.h:248
TTrainController::ContinuationTrainExpectationMultiMap
TContinuationTrainExpectationMultiMap ContinuationTrainExpectationMultiMap
Multimap for TContinuationTrainExpectationEntry objects, the access key is the expectation time.
Definition: TrainUnit.h:852
TTrain::StoppedForTrainInFront
bool StoppedForTrainInFront
Definition: TrainUnit.h:472
GapJump
@ GapJump
Definition: TrackUnit.h:65
TRailGraphics::CodeK
Graphics::TBitmap * CodeK
Definition: GraphicUnit.h:988
TTrainController::MissedStops
int MissedStops
Definition: TrainUnit.h:821
NoSequence
@ NoSequence
Definition: TrainUnit.h:75
clCallOnBackground
#define clCallOnBackground
Definition: GraphicUnit.h:292
TTrackElement::Length01
int Length01
Definition: TrackUnit.h:148
TTrackElement::SpeedLimit01
int SpeedLimit01
Definition: TrackUnit.h:148
TTrain::TerminatedMessageSent
bool TerminatedMessageSent
set when a 'train terminated' message has been logged, to prevent its being logged more than once
Definition: TrainUnit.h:403
Finish
@ Finish
Definition: TrainUnit.h:75
TTrain::FinishJoin
void FinishJoin(int Caller)
Carry out the actions needed when a train is waiting to join another train.
Definition: TrainUnit.cpp:6078
TTrainController::FinishedOperation
void FinishedOperation(int Caller)
called when exiting operation mode to delete all trains and timetable data etc
Definition: TrainUnit.cpp:9239
TTrain::MidElement
int MidElement
Definition: TrainUnit.h:362
TTrackElement::TempTrackMarker23
bool TempTrackMarker23
Utility markers for program use.
Definition: TrackUnit.h:138
TAllRoutes::CallonVector
std::vector< TCallonEntry > CallonVector
the store of all call-on entries
Definition: TrackUnit.h:1658
TTrain::LeadElement
int LeadElement
Definition: TrainUnit.h:362
clDerailedBackground
#define clDerailedBackground
Definition: GraphicUnit.h:294
TTrain::RepeatNumber
int RepeatNumber
indicates which of the repeating services this train represents (0 = first service)
Definition: TrainUnit.h:356
TTrainDataEntry::ActionVector
TActionVector ActionVector
all the actions for the train
Definition: TrainUnit.h:223
TTrainController::BaseTime
TDateTime BaseTime
CurrentDateTime (i.e. real time) when operation restarts after a pause.
Definition: TrainUnit.h:684
TTrain::StartSpeed
int StartSpeed
the speed of the train when introduced into the railway (in km/h)
Definition: TrainUnit.h:360
TExitInfo::TExitInfo
TExitInfo()
Definition: TrainUnit.cpp:62
TTrainController::LoadSessionLockedRoutes
void LoadSessionLockedRoutes(int Caller, std::ifstream &SessionFile)
load locked routes from a session file
Definition: TrainUnit.cpp:15263
TTrainController::EarlyPasses
int EarlyPasses
Definition: TrainUnit.h:814
TTrain::HeadCode
AnsiString HeadCode
needs own HeadCode because repeat entries will differ from TrainDataEntry.HeadCode
Definition: TrainUnit.h:321
TDisplay::GetOutputLog7
TLabel * GetOutputLog7()
Definition: DisplayUnit.h:175
TTrainController::TOpTimeToActMultiMapEntry
std::pair< float, THCandTrainPosParam > TOpTimeToActMultiMapEntry
Definition: TrainUnit.h:766
FailMissedSplit
@ FailMissedSplit
Definition: TrainUnit.h:41
TTrain::EntryTime
TDateTime EntryTime
Definition: TrainUnit.h:452
TExitInfo::RepeatNumber
short RepeatNumber
Definition: TrainUnit.h:107
TTrainController::TContinuationAutoSigEntry::FirstDelay
double FirstDelay
Definition: TrainUnit.h:699
TTrainController::SaveTrainDataVectorToFile
void SaveTrainDataVectorToFile(int Caller)
diagnostic function to store all train data to a file for examination, not used normally
Definition: TrainUnit.cpp:14986
TOnePrefDir::PrefDirSize
unsigned int PrefDirSize() const
Return the vector size.
Definition: TrackUnit.h:1374
TTrack::PlotSignal
void PlotSignal(int Caller, TTrackElement TrackElement, TDisplay *Disp)
Plot signals on screen according to their aspect (Attribute value)
Definition: TrackUnit.cpp:6025
End
@ End
Definition: TrackUnit.h:75
TTrain::OneLengthAccelDecel
bool OneLengthAccelDecel
set when a train can only move forwards one element before stopping but needs to accelerate for the f...
Definition: TrainUnit.h:393
TTrain::FloatingLabelNextString
AnsiString FloatingLabelNextString(int Caller, TActionVectorEntry *Ptr)
Used in the floating window to display the 'Next' action.
Definition: TrainUnit.cpp:6914
TTrack::TimetabledLocationNameAllocated
bool TimetabledLocationNameAllocated(int Caller, AnsiString LocationName)
True if a non-empty LocationName found as a timetabled location name i.e. not as a continuation name.
Definition: TrackUnit.cpp:8721
TTrain::FailedTrainNoFinishJoinMessage
bool FailedTrainNoFinishJoinMessage
Definition: TrainUnit.h:337
TTrack::InactiveTrackElementAt
TTrackElement & InactiveTrackElementAt(int Caller, int At)
A range-checked version of InactiveTrackVector.at(At)
Definition: TrackUnit.cpp:10474
ExitRailway
@ ExitRailway
Definition: TrainUnit.h:65
TPrefDirElement::GetTrackVectorPosition
unsigned int GetTrackVectorPosition() const
Returns TrackVectorPosition.
Definition: TrackUnit.h:299
TTrain::StationStopCalculated
bool StationStopCalculated
used in calculating DistanceToStationStop for trains running early before they have reached the stop ...
Definition: TrainUnit.h:399
TTrainController::LoadSessionContinuationAutoSigEntries
void LoadSessionContinuationAutoSigEntries(int Caller, std::ifstream &SessionFile)
load ContinuationAutoSigEntries from a session file
Definition: TrainUnit.cpp:15346
TTrackElement
Basic track elements as implemented in the overall railway layout.
Definition: TrackUnit.h:124
TRailGraphics::Code_n
Graphics::TBitmap * Code_n
Definition: GraphicUnit.h:955
FailMissedNewService
@ FailMissedNewService
Definition: TrainUnit.h:42
TUtilities::Format96HHMMSS
AnsiString Format96HHMMSS(TDateTime DateTime)
formats a TDateTime into an AnsiString of the form hh:mm:ss where hh runs from 00 to 95 & resets when...
Definition: Utilities.cpp:788
TRailGraphics::smSolidBgnd
Graphics::TBitmap * smSolidBgnd
Definition: GraphicUnit.h:1005
clTRSBackground
#define clTRSBackground
Definition: GraphicUnit.h:303
TTrainController::OtherMissedEvents
int OtherMissedEvents
Definition: TrainUnit.h:826
TTrainDataEntry::Description
AnsiString Description
headcode is the first train's headcode, rest are calculated from repeat information; ServiceReference...
Definition: TrainUnit.h:207
TTrainController::EarlyExits
int EarlyExits
Definition: TrainUnit.h:815
TTrain::ExitSpeedHalf
double ExitSpeedHalf
speed when half way into the next element
Definition: TrainUnit.h:415
SignalPost
@ SignalPost
Definition: TrackUnit.h:65
TRailGraphics::Code_h
Graphics::TBitmap * Code_h
Definition: GraphicUnit.h:949
TTrain::LastActionTime
TDateTime LastActionTime
time of the last timetabled event, used to ensure at least a 30 second delay before the next action
Definition: TrainUnit.h:456
TDisplay::GetOutputLog1
TLabel * GetOutputLog1()
Return pointers to warning message logs (appear above the railway display during operation)
Definition: DisplayUnit.h:144
TAllRoutes::TRoute2MultiMapIterator
TRoute2MultiMap::iterator TRoute2MultiMapIterator
Definition: TrackUnit.h:1635
TTrain::TrainFailurePending
bool TrainFailurePending
set when failure due & takes effect when all PlotElements properly set, added at v2....
Definition: TrainUnit.h:345
FailMissedTerminate
@ FailMissedTerminate
Definition: TrainUnit.h:42
TTrainController::SecondPassActions
bool SecondPassActions(int Caller, bool GiveMessages, bool &TwoLocationFlag)
Carry out further detailed timetable consistency checks, return true for success.
Definition: TrainUnit.cpp:11865
TRailGraphics::Code9
Graphics::TBitmap * Code9
Definition: GraphicUnit.h:977
TTrainDataEntry::HeadCode
AnsiString HeadCode
Definition: TrainUnit.h:207
TTrain::TrainHasFailed
void TrainHasFailed(int Caller)
Called when there is a random train failure.
Definition: TrainUnit.cpp:5406
clNormalBackground
#define clNormalBackground
Definition: GraphicUnit.h:297
TActionVectorEntry::NonRepeatingShuttleLinkEntryPtr
TTrainDataEntry * NonRepeatingShuttleLinkEntryPtr
pointer used by shuttles for the non-shuttle train links, in & out, the corresponding non-shuttle lin...
Definition: TrainUnit.h:148
TTrainController::SPADRisks
int SPADRisks
Definition: TrainUnit.h:829
TTrainController::TLocServiceTimesVector
std::vector< TLocServiceTimes > TLocServiceTimesVector
Definition: TrainUnit.h:752
TTrainController::LocServiceTimesAtLocTimeSort
bool LocServiceTimesAtLocTimeSort(TLocServiceTimes i, TLocServiceTimes j)
Definition: TrainUnit.h:879
TTrain::EntrySpeed
double EntrySpeed
speed at which the train enters the next element
Definition: TrainUnit.h:413
TConfiguration
TConfiguration
< describes the type of track link. 'End' is used for both buffer stop and continuation entry/exit po...
Definition: TrackUnit.h:74
TTrain::TrainGone
bool TrainGone
set when train has left the railway, so it can be removed from the display at the next clock tick
Definition: TrainUnit.h:468
TTrackType
TTrackType
< describes the type of track element
Definition: TrackUnit.h:64
TTrainController::OnTimePasses
int OnTimePasses
Definition: TrainUnit.h:824
TTrainController::SigSHigh
bool SigSHigh
Definition: TrainUnit.h:789
TRailGraphics::smCaramel
Graphics::TBitmap * smCaramel
Definition: GraphicUnit.h:893
TGraphicElement::PlotOriginal
void PlotOriginal(int Caller, TDisplay *Disp)
Plot the original graphic on screen.
Definition: TrackUnit.cpp:1878
TExitInfo
Definition: TrainUnit.h:104
clBufferAttentionNeeded
#define clBufferAttentionNeeded
Definition: GraphicUnit.h:290
TTrain::StoppedAfterSPAD
bool StoppedAfterSPAD
Definition: TrainUnit.h:472
FailSPAD
@ FailSPAD
Definition: TrainUnit.h:40
TTrainController::OnTimeArrivals
int OnTimeArrivals
Definition: TrainUnit.h:822
clTrainFailedBackground
#define clTrainFailedBackground
Definition: GraphicUnit.h:304
TRailGraphics::Code_x
Graphics::TBitmap * Code_x
Definition: GraphicUnit.h:965
clFrontCodeSignaller
#define clFrontCodeSignaller
Definition: GraphicUnit.h:295
TimeCmdHeadCode
@ TimeCmdHeadCode
Definition: TrainUnit.h:64
Utilities
TUtilities * Utilities
Definition: Utilities.cpp:47
TFixedTrackPiece::SmallGraphicPtr
Graphics::TBitmap * SmallGraphicPtr
the track bitmap for display on the zoomed-out railway
Definition: TrackUnit.h:93
TActionVectorEntry::ArrivalTime
TDateTime ArrivalTime
Definition: TrainUnit.h:134
TTrain::StoppedAtBuffers
bool StoppedAtBuffers
Definition: TrainUnit.h:472
TTrainController::CreateTTAnalysisFile
bool CreateTTAnalysisFile(int Caller, AnsiString RailwayTitle, AnsiString TimetableTitle, AnsiString CurDir, bool ArrChecked, bool DepChecked, bool AtLocChecked, bool DirChecked, int ArrRange, int DepRange)
Generate a timetable analysis file in the 'Formatted Timetables' folder, return false if failed for a...
Definition: TrainUnit.cpp:16143
TRailGraphics::Code_b
Graphics::TBitmap * Code_b
Definition: GraphicUnit.h:943
TTrain::Mass
int Mass
in kg
Definition: TrainUnit.h:446
TRailGraphics::Code0
Graphics::TBitmap * Code0
Definition: GraphicUnit.h:968
TTrainController::ProcessOneTimetableLine
bool ProcessOneTimetableLine(int Caller, int Count, AnsiString OneLine, bool &EndOfFile, bool FinalCall, bool GiveMessages, bool CheckLocationsExistInRailway)
Carry out preliminary (mainly syntax) validity checks on a single timetable service entry and (if Fin...
Definition: TrainUnit.cpp:10139
TTrainController::TContinuationTrainExpectationEntry::IncrementalMinutes
int IncrementalMinutes
Repeat separation in minutes.
Definition: TrainUnit.h:723
TTrain::LeadExitPos
int LeadExitPos
Definition: TrainUnit.h:362
TTrain::FrontElementLength
int FrontElementLength
values associated with the element immediately in front of the train (speed in km/h,...
Definition: TrainUnit.h:444
TTimetableShuttleLinkType
TTimetableShuttleLinkType
Definition: TrainUnit.h:79
TRailGraphics::CodeP
Graphics::TBitmap * CodeP
Definition: GraphicUnit.h:993
Pass
@ Pass
Definition: TrainUnit.h:52
TTrain::SkipPtrValue
int SkipPtrValue
stores the pointer increment from first action in ActionVector for skipped actions when a departure i...
Definition: TrainUnit.h:371
TNumList
std::list< int > TNumList
a list of valid train exit TrackVector positions for 'Fer' entries
Definition: TrainUnit.h:91
TTrainController::DerailWarning
bool DerailWarning
Definition: TrainUnit.h:775
TTrainController::TContinuationTrainExpectationMultiMapPair
std::pair< TDateTime, TContinuationTrainExpectationEntry > TContinuationTrainExpectationMultiMapPair
a single multimap entry
Definition: TrainUnit.h:737
RouteForceCancelled
@ RouteForceCancelled
Definition: TrainUnit.h:44
EnRoute
@ EnRoute
Definition: TrainUnit.h:70
TTrain::IsTrainIDOnBridgeTrackPos23
bool IsTrainIDOnBridgeTrackPos23(int Caller, unsigned int TrackVectorPosition)
True if train is on a bridge on trackpos 2 & 3.
Definition: TrainUnit.cpp:3024
TTrack::DiagonalFouledByTrain
bool DiagonalFouledByTrain(int Caller, int HLoc, int VLoc, int DiagonalLinkNumber, int &TrainID)
As DiagonalFouledByRouteOrTrain (in TAllRoutes) but only checks for a train (may or may not be a rout...
Definition: TrackUnit.cpp:11197
TTrainController::TotLateDepMins
float TotLateDepMins
Definition: TrainUnit.h:806
TDisplay::ZoomOutFlag
bool ZoomOutFlag
true when zoomed-out
Definition: DisplayUnit.h:70
TRailGraphics::CodeC
Graphics::TBitmap * CodeC
Definition: GraphicUnit.h:980
TTrain::PlotElement
int PlotElement[4]
the TrackVectorPosition of the element where each of the 4 headcode characters is plotted (need to be...
Definition: TrainUnit.h:479
TTrainController::TServiceCallingLocsList
std::list< AnsiString > TServiceCallingLocsList
Used in determining train directions in timetable conflict analysis.
Definition: TrainUnit.h:755
TTrainController::TrainVector
TTrainVector TrainVector
vector containing all trains currently in the railway
Definition: TrainUnit.h:860
TTrack::ResetAllTrainIDElements
void ResetAllTrainIDElements(int Caller)
Track elements have members that indicates whether and on what track a train is present (TrainIDOnEle...
Definition: TrackUnit.cpp:7553
TRailGraphics::Code_l
Graphics::TBitmap * Code_l
Definition: GraphicUnit.h:953
TTrainController::TContinuationAutoSigEntry
< TTClockTime when last session saved - to prevent display of warning message on exit session if < 5 ...
Definition: TrainUnit.h:697
TUtilities::CallLogPop
void CallLogPop(int Caller)
pops the last entry off the call stack, throws an error if called when empty
Definition: Utilities.cpp:50
TTrain::IncrementalMinutes
int IncrementalMinutes
the number of minutes to increment by in repeat entries
Definition: TrainUnit.h:350
TTrain::MaximumSpeedLimit
static const int MaximumSpeedLimit
km/h
Definition: TrainUnit.h:315
TRailGraphics::CodeI
Graphics::TBitmap * CodeI
Definition: GraphicUnit.h:986
TTrainController::TotEarlyArrMins
float TotEarlyArrMins
values for performance file summary
Definition: TrainUnit.h:801
TTrain::WriteTrainToImage
void WriteTrainToImage(int Caller, Graphics::TBitmap *Bitmap)
Called by TTrainController::WriteTrainsToImage (called by TInterface::SaveOperatingImage1Click) to ad...
Definition: TrainUnit.cpp:8421
TRailGraphics::CodeL
Graphics::TBitmap * CodeL
Definition: GraphicUnit.h:989
TTrainDataEntry::MaxBrakeRate
double MaxBrakeRate
in metres/sec/sec
Definition: TrainUnit.h:209
FNSNonRepeatToShuttle
@ FNSNonRepeatToShuttle
Definition: TrainUnit.h:64
TDisplay::PlotOutput
void PlotOutput(int Caller, int HPos, int VPos, Graphics::TBitmap *PlotItem)
Plot the graphic at screen position HPos & VPos.
Definition: DisplayUnit.cpp:85
TUtilities::SaveFileBool
void SaveFileBool(std::ofstream &OutFile, bool SaveBool)
stores '1' if the bool is true or '0' if false to the file, then a CR
Definition: Utilities.cpp:108
TTrainDataEntry::SignallerSpeed
int SignallerSpeed
in km/h for use when under signaller control
Definition: TrainUnit.h:219
TTrain::AbleToMove
bool AbleToMove(int Caller)
Indicates that a train is not prevented from moving - used to allow appropriate popup menu options wh...
Definition: TrainUnit.cpp:6770
TTrainController::MTBFHours
double MTBFHours
Mean time between failures in timetable clock hours.
Definition: TrainUnit.h:792
TOneRoute::ForceCancelRoute
void ForceCancelRoute(int Caller)
Cancel a route immediately if a train occupies it when travelling in the wrong direction (or occupies...
Definition: TrackUnit.cpp:17314
TTrain::TrainMode
TTrainMode TrainMode
mode of operation - either Timetable (running under timetable control) or Signaller (running under si...
Definition: TrainUnit.h:460
TTrainController::CheckNonRepeatingShuttleLinkTime
bool CheckNonRepeatingShuttleLinkTime(int Caller, TDateTime ReverseEventTime, TDateTime ForwardEventTime, int RepeatMins, int RepeatNumber)
The forward train is the finish shuttle entry 'Fns-sh', the reverse (new non-repeating service) time ...
Definition: TrainUnit.cpp:14655
TTrack::ThisNamedLocationLongEnoughForSplit
bool ThisNamedLocationLongEnoughForSplit(int Caller, AnsiString LocationName, int FirstNamedElementPos, int &SecondNamedElementPos, int &FirstNamedLinkedElementPos, int &SecondNamedLinkedElementPos)
See above under 'OneNamedLocationLongEnoughForSplit'.
Definition: TrackUnit.cpp:10615
TUtilities::Format96HHMM
AnsiString Format96HHMM(TDateTime DateTime)
formats a TDateTime into an AnsiString of the form hh:mm where hh runs from 00 to 95 & resets when it...
Definition: Utilities.cpp:807
TTrain::CallOnMaxSpeed
static const int CallOnMaxSpeed
km/h
Definition: TrainUnit.h:309
TTrain::CheckAndCancelRouteForWrongEndEntry
void CheckAndCancelRouteForWrongEndEntry(int Caller, int Element, int EntryPos)
Checks whether Element and EntryPos (where train is about to enter) is on an existing route (or cross...
Definition: TrainUnit.cpp:3232
TRailGraphics::CodeW
Graphics::TBitmap * CodeW
Definition: GraphicUnit.h:1000
TTrain::ReleaseTime
TDateTime ReleaseTime
Definition: TrainUnit.h:454
TTrain::GetTrainHeadCode
AnsiString GetTrainHeadCode(int Caller)
Returns the train headcode, taking account of the RepeatNumber.
Definition: TrainUnit.cpp:4996
TTrain::LowEntryValue
bool LowEntryValue(int EntryLink) const
Returns true if EntryLink is 1, 2, 4 or 7, in these circumstances the front of the train (i....
Definition: TrainUnit.cpp:2545
RearSplit
@ RearSplit
Definition: TrainUnit.h:50
TActionVectorEntry::FrontStartOrRepeatDigits
int FrontStartOrRepeatDigits
dual-purpose variables used for the TrackVectorPositions of the rear and front train starting element...
Definition: TrainUnit.h:132
SignallerControlStop
@ SignallerControlStop
Definition: TrainUnit.h:52
TTrack::TrackVector
TTrackVector TrackVector
Definition: TrackUnit.h:801
TTrain::HasTrainGone
bool HasTrainGone()
Check whether the train has left the railway, so that it can be removed from the display at the next ...
Definition: TrainUnit.h:647
TRailGraphics::Code_i
Graphics::TBitmap * Code_i
Definition: GraphicUnit.h:950
TTrainController::ExcessLCDownMins
float ExcessLCDownMins
total excess time in minutes over the 3 minutes barriers down allowance for level crossings
Definition: TrainUnit.h:799
TRailGraphics::Code5
Graphics::TBitmap * Code5
Definition: GraphicUnit.h:973
TTrackElement::CallingOnSet
bool CallingOnSet
Used for for signals only when a train is being called on - used to plot the position lights.
Definition: TrackUnit.h:134
TUtilities::SaveFileInt
void SaveFileInt(std::ofstream &OutFile, int SaveInt)
stores the int value to the file, then a CR
Definition: Utilities.cpp:121
TrainController
TTrainController * TrainController
the object pointer, one object only - created in InterfaceUnit
Definition: TrainUnit.cpp:54
TTrack::OneNamedLocationLongEnoughForSplit
bool OneNamedLocationLongEnoughForSplit(int Caller, AnsiString LocationName)
Definition: TrackUnit.cpp:10509
TTrain::BufferAtExit
bool BufferAtExit(int Caller, int Element, int Exitpos) const
True if Element is a buffer and Exitpos is the buffer end.
Definition: TrainUnit.cpp:2956
TTrain::IsTrainTerminating
bool IsTrainTerminating(int Caller)
True if train service terminates at its current location.
Definition: TrainUnit.cpp:6742
TTrainController::SecondPassMessage
void SecondPassMessage(bool GiveMessages, AnsiString Message)
Give a user message during timetable integrity checking if GiveMessages is true, ignore if false.
Definition: TrainUnit.cpp:14753
SignallerStepForward
@ SignallerStepForward
Definition: TrainUnit.h:52
TTrack::GetHLocMin
int GetHLocMin()
Definition: TrackUnit.h:867
TTrainController::CheckCrossReferencesAndSetData
bool CheckCrossReferencesAndSetData(int Caller, AnsiString SoughtHeadCode, AnsiString SeekingHeadCode, bool Shuttle, bool GiveMessages)
A timetable validation function where all service cross references are checked for validity and set p...
Definition: TrainUnit.cpp:13416
TDisplay::GetOutputLog5
TLabel * GetOutputLog5()
Definition: DisplayUnit.h:165
TTrainController::LateExits
int LateExits
Definition: TrainUnit.h:820
TTrainController::OpTimeToActUpdateCounter
unsigned int OpTimeToActUpdateCounter
<List of all ServiceRefs that have two or more same locations without a cdt between - loaded during S...
Definition: TrainUnit.h:844
TTrain::BeingCalledOn
bool BeingCalledOn
in course of being called on to a station
Definition: TrainUnit.h:377
TUtilities::CheckFileInt
bool CheckFileInt(std::ifstream &InFile, int Lowest, int Highest)
checks that the value is an int lying between Lowest & Highest (inclusive), returns true for success
Definition: Utilities.cpp:238
TRailGraphics::ChangeForegroundColour2
void ChangeForegroundColour2(int Caller, Graphics::TBitmap *BitmapIn, Graphics::TBitmap *BitmapOut, TColor NewForegroundColour, TColor BackgroundColour)
New function to do the same as the above but with fewer pixel changes - for use in LoadSession to avo...
Definition: GraphicUnit.cpp:3449
TRailGraphics::Code_e
Graphics::TBitmap * Code_e
Definition: GraphicUnit.h:946
TTrain::CalcTimeToAct
float CalcTimeToAct(int Caller, float &TimeToExit, THVShortPair &ExitPair)
new v2.2.0 for operator action panel. Calculates the time left for operator action to avoid unnecessa...
Definition: TrainUnit.cpp:8485
TTrain::HoldAtLocationInTTMode
bool HoldAtLocationInTTMode
true if actions are needed before train departs
Definition: TrainUnit.h:327
TTrain::PlotTrainInZoomOutMode
void PlotTrainInZoomOutMode(int Caller, bool Flash)
Plots the train on screen in zoomed-out mode, state of 'Flash' determines whether the flashing trains...
Definition: TrainUnit.cpp:8231
TTrain::LoadOneSessionTrain
void LoadOneSessionTrain(int Caller, std::ifstream &InFile)
Create one train with relevant member values from the sesion file.
Definition: TrainUnit.cpp:7510
TTrain::PlotBackgroundGraphic
void PlotBackgroundGraphic(int Caller, int ArrayNumber, TDisplay *Disp) const
Replot the graphic pointed to by BackgroundPtr (see above) after a train has passed.
Definition: TrainUnit.cpp:2948
TTrainController::TotLateArrMins
float TotLateArrMins
Definition: TrainUnit.h:805
TTrain::HOffset
int HOffset[4]
Definition: TrainUnit.h:475
FailMissedArrival
@ FailMissedArrival
Definition: TrainUnit.h:41
TTrain::UpdateCounter
unsigned int UpdateCounter
used in train splitting operations to prevent too frequent checks for a location being long enough fo...
Definition: TrainUnit.h:450
NewService
@ NewService
Definition: TrainUnit.h:50
FailEnterLockedRoute
@ FailEnterLockedRoute
Definition: TrainUnit.h:42
TTrainController::PlotAllTrainsInZoomOutMode
void PlotAllTrainsInZoomOutMode(int Caller, bool Flash)
Plots all trains on screen in zoomed-out mode, state of 'Flash' determines whether the flashing train...
Definition: TrainUnit.cpp:15499
TOneRoute
A descendent of TOnePrefDir used for routes. Used during contruction of a route (ConstructRoute) and ...
Definition: TrackUnit.h:1474
TTrain::SignallerStopped
bool SignallerStopped
Definition: TrainUnit.h:472
TTrain::BufferZoomOutFlashRequired
bool BufferZoomOutFlashRequired
set when train is at buffers and is to flash in zoomout mode (i.e. when reaches buffers unexpectedly ...
Definition: TrainUnit.h:379
TAllRoutes::TRouteType
TRouteType
Definition: TrackUnit.h:1618
TTrain::TrainDataEntryPtr
TTrainDataEntry * TrainDataEntryPtr
points to the current position in the timetable's TrainDataVector
Definition: TrainUnit.h:367
TTrainDataEntry::StartSpeed
int StartSpeed
in km/h
Definition: TrainUnit.h:221
TTrainDataEntry::NumberOfTrains
int NumberOfTrains
number of repeats + 1
Definition: TrainUnit.h:217
TTrainController::Derailments
int Derailments
Definition: TrainUnit.h:812
clStationStopBackground
#define clStationStopBackground
Definition: GraphicUnit.h:301
TDisplay::GetOutputLog6
TLabel * GetOutputLog6()
Definition: DisplayUnit.h:170
ShuttleLink
@ ShuttleLink
Definition: TrainUnit.h:80
TRailGraphics::CodeM
Graphics::TBitmap * CodeM
Definition: GraphicUnit.h:990
Crossover
@ Crossover
Definition: TrackUnit.h:65
TTrain::ClearToNextSignal
bool ClearToNextSignal(int Caller)
Checks forward from train LeadElement, following leading point attributes but ignoring trailing point...
Definition: TrainUnit.cpp:4605
TTrainController::TContinuationTrainExpectationEntry::Description
AnsiString Description
service description
Definition: TrainUnit.h:717
AtLocation
@ AtLocation
Definition: TrainUnit.h:70
Signal
@ Signal
Definition: TrackUnit.h:75
TRailGraphics::Code_k
Graphics::TBitmap * Code_k
Definition: GraphicUnit.h:952
TRailGraphics::CodeF
Graphics::TBitmap * CodeF
Definition: GraphicUnit.h:983
TTrack::ContinuationNameMap
std::map< AnsiString, char > ContinuationNameMap
map of all continuation names, char is a dummy
Definition: TrackUnit.h:772
TTrackElement::TrainIDOnBridgeTrackPos01
int TrainIDOnBridgeTrackPos01
Definition: TrackUnit.h:152
TAllRoutes::GetRouteTypeAndNumber
TRouteType GetRouteTypeAndNumber(int Caller, int TrackVectorPosition, int LinkPos, int &RouteNumber)
Examines Route2MultiMap and if the element at TrackVectorPosition with LinkPos (can be entry or exit)...
Definition: TrackUnit.cpp:17831
TTrainController::TrainDataVector
TTrainDataVector TrainDataVector
vector containing the internal timetable
Definition: TrainUnit.h:858
TTrainController::ConsolidateSARNTAtLoc
AnsiString ConsolidateSARNTAtLoc(int Caller, const AnsiString Input, int &NumTrainsAtLoc)
Removes duplicates from and sorts ServiceAndRepeatNumTotal into alphabetical order for AtLoc listing ...
Definition: TrainUnit.cpp:18521
TAllRoutes::NotAutoSigsRoute
@ NotAutoSigsRoute
Definition: TrackUnit.h:1619
TTrain::ExitTimeHalf
TDateTime ExitTimeHalf
Definition: TrainUnit.h:452
Exited
@ Exited
Definition: TrainUnit.h:86
TTrack::TrackElementAt
TTrackElement & TrackElementAt(int Caller, int At)
A range-checked version of TrackVector.at(At)
Definition: TrackUnit.cpp:10460
TTrain::ActionsSkippedFlag
bool ActionsSkippedFlag
prevents any further skipping until after the next departure
Definition: TrainUnit.h:325
TTrainController::LoadSessionTrains
void LoadSessionTrains(int Caller, std::ifstream &SessionFile)
load trains from a session file
Definition: TrainUnit.cpp:15197
TTrain::CheckOneSessionTrain
static bool CheckOneSessionTrain(std::ifstream &InFile)
Carries out an integrity check for the train section of a session file, if fails a message is given a...
Definition: TrainUnit.cpp:7797
TAllRoutes::GetRouteTypeAndGraphics
TRouteType GetRouteTypeAndGraphics(int Caller, int TrackVectorPosition, int LinkPos, Graphics::TBitmap *&EXGraphicPtr, Graphics::TBitmap *&EntryDirectionGraphicPtr)
Examines Route2MultiMap for the element at TrackVectorPosition with LinkPos (can be entry or exit).
Definition: TrackUnit.cpp:17657
TRailGraphics::CodeY
Graphics::TBitmap * CodeY
Definition: GraphicUnit.h:1002
TTrain::SetOneGraphicCode
Graphics::TBitmap * SetOneGraphicCode(char CodeChar)
Return a pointer to the graphic corresponding to the character 'CodeVhar'.
Definition: TrainUnit.cpp:2162
TTrain::DerailPending
bool DerailPending
Definition: TrainUnit.h:472
TTrainMode
TTrainMode
indicates train operating mode, 'None' for not in use
Definition: TrainUnit.h:57
TAllRoutes::TCallonEntry
Used to store relevant values when a call-on found, ready for plotting an unrestricted route.
Definition: TrackUnit.h:1640
TRailGraphics::Code_a
Graphics::TBitmap * Code_a
Definition: GraphicUnit.h:942
TTrain::StoppedAtLocation
bool StoppedAtLocation
Definition: TrainUnit.h:472
TTrainController::TContinuationTrainExpectationMultiMapIterator
TContinuationTrainExpectationMultiMap::iterator TContinuationTrainExpectationMultiMapIterator
iterator for the multimap
Definition: TrainUnit.h:735
TTrain::MaximumMassLimit
static const int MaximumMassLimit
kg (i.e. 10,000 tonnes)
Definition: TrainUnit.h:311
TTrainController::StopTTClockFlag
bool StopTTClockFlag
when true the timetable clock is stopped, used for messages display and train popup menu display etc
Definition: TrainUnit.h:777
FrontSplit
@ FrontSplit
Definition: TrainUnit.h:50
TTrain::MaxExitSpeed
double MaxExitSpeed
the maximum speed that the train can exit the next element
Definition: TrainUnit.h:423
TTrainController::BFHigh
bool BFHigh
Definition: TrainUnit.h:789
TRailGraphics::Code4
Graphics::TBitmap * Code4
Definition: GraphicUnit.h:972
TRailGraphics::Code_g
Graphics::TBitmap * Code_g
Definition: GraphicUnit.h:948
TTrain::LagEntryPos
int LagEntryPos
Definition: TrainUnit.h:362
SNTShuttle
@ SNTShuttle
Definition: TrainUnit.h:64
TRailGraphics::CodeG
Graphics::TBitmap * CodeG
Definition: GraphicUnit.h:984
Leave
@ Leave
Definition: TrainUnit.h:50
TTrainController::UnexpectedExits
int UnexpectedExits
Definition: TrainUnit.h:831
TTrainController::TLocServiceTimes::AtLocTime
AnsiString AtLocTime
Definition: TrainUnit.h:747
TTrainController::ServiceReference
AnsiString ServiceReference
String used to display the offending service in timetable error messages.
Definition: TrainUnit.h:773
TTrain::FirstLaterStopRecoverableTime
float FirstLaterStopRecoverableTime
this used to deduct from RecoverableTime when arrive at a location for OperatorActionpanel
Definition: TrainUnit.h:442
TTrain::LeavingUnderSigControlAtContinuation
bool LeavingUnderSigControlAtContinuation
set when the train has reached an exit continuation when under signaller control, used to prevent the...
Definition: TrainUnit.h:391
TTrain::PlotTrainGraphic
void PlotTrainGraphic(int Caller, int ArrayNumber, TDisplay *Disp)
Plot the train's headcode character corresponding to ArrayNumber.
Definition: TrainUnit.cpp:2929
TRailGraphics::Code_j
Graphics::TBitmap * Code_j
Definition: GraphicUnit.h:951
NoFormat
@ NoFormat
Definition: TrainUnit.h:64
TTrainController::TwoOrMoreLocationsWarningGiven
bool TwoOrMoreLocationsWarningGiven
new at v2.6.0 to allow loops
Definition: TrainUnit.h:785
FailBufferCrash
@ FailBufferCrash
Definition: TrainUnit.h:43
WaitingForFJO
@ WaitingForFJO
Definition: TrainUnit.h:43
FailMissedExitRailway
@ FailMissedExitRailway
Definition: TrainUnit.h:42
TimeTimeLoc
@ TimeTimeLoc
Definition: TrainUnit.h:64
TTrain::GetOffsetValues
void GetOffsetValues(int Caller, int &HOffset, int &VOffset, int Link) const
Sets HOffset & VOffset (see above) for a single headcode character depending on the Link value.
Definition: TrainUnit.cpp:2474
TTrain::TimeToExit
float TimeToExit
in minutes: new for multiplayer, -1 = > 60 mins
Definition: TrainUnit.h:440
TTrain::Derailed
bool Derailed
Definition: TrainUnit.h:472
TRailGraphics::ChangeSpecificColour
void ChangeSpecificColour(int Caller, Graphics::TBitmap *BitmapIn, Graphics::TBitmap *BitmapOut, TColor ColourToBeChanged, TColor NewColour)
Definition: GraphicUnit.cpp:3478
Enter
@ Enter
Definition: TrainUnit.h:50
TRailGraphics::bm93set
Graphics::TBitmap * bm93set
Definition: GraphicUnit.h:514
TTrack::GetTrackVectorPositionFromString
int GetTrackVectorPositionFromString(int Caller, AnsiString String, bool GiveMessages)
Takes the ElementID value (an AnsiString) (e.g. "8-13", "N43-N127", etc) and returns the correspondin...
Definition: TrackUnit.cpp:7768
TRailGraphics::Code_q
Graphics::TBitmap * Code_q
Definition: GraphicUnit.h:958
TTrain::NewTrainService
void NewTrainService(int Caller)
Carry out the actions needed when a train forms a new service (code Fns)
Definition: TrainUnit.cpp:6272
TTrainController::SaveSessionLockedRoutes
void SaveSessionLockedRoutes(int Caller, std::ofstream &SessionFile)
save locked routes to a session file
Definition: TrainUnit.cpp:15246
TTrain::OriginalPowerAtRail
double OriginalPowerAtRail
new at v2.4.0 to store value before a failure so it can be restored from here when repaired
Definition: TrainUnit.h:434
TTrainFormattedInformation
Contains all information for a single timetable entry for use in the formatted timetable.
Definition: TrainUnit.h:274
TTrainController::IsSNTEntryLocated
bool IsSNTEntryLocated(int Caller, const TTrainDataEntry &TDEntry, AnsiString &LocationName)
New trains introduced with 'Snt' may be at a timetabled location or elsewhere. This function checks a...
Definition: TrainUnit.cpp:13946
TTrainController::TContinuationTrainExpectationEntry::VectorPosition
int VectorPosition
TrackVectorPosition for the continuation element.
Definition: TrainUnit.h:727
TTrainController::TTrainController
TTrainController()
Constructor.
Definition: TrainUnit.cpp:8879
Timetable
@ Timetable
Definition: TrainUnit.h:58
TUtilities::SaveFileDouble
void SaveFileDouble(std::ofstream &OutFile, double SaveDouble)
converts the double value to a string (if double stored directly it is truncated to 6 digits) then st...
Definition: Utilities.cpp:127
TTrainController::GetRepeatHeadCode
AnsiString GetRepeatHeadCode(int Caller, AnsiString BaseHeadCode, int RepeatNumber, int IncDigits)
Return the service headcode for the repeat service.
Definition: TrainUnit.cpp:14325
TTrain::AbleToMoveButForSignal
bool AbleToMoveButForSignal(int Caller)
Indicates that a train is only prevented from moving by a signal - used to allow appropriate popup me...
Definition: TrainUnit.cpp:6825
TTrainController::OpActionPanelVisible
bool OpActionPanelVisible
new v2.2.0 flag to prevent time to act functions when not visible
Definition: TrainUnit.h:783
TTrack::GetAnyElementOppositeLinkPos
int GetAnyElementOppositeLinkPos(int Caller, int TrackVectorPosition, int LinkPos, bool &Derail)
Return the opposite link position for the element at TrackVectorPosition with link position LinkPos,...
Definition: TrackUnit.cpp:11048
TTrack::GapFlashRedPosition
int GapFlashRedPosition
TrackVectorPosition of the gap element that is flashing green or red.
Definition: TrackUnit.h:764
TActionVectorEntry::NumberOfRepeats
int NumberOfRepeats
the number of repeating services
Definition: TrainUnit.h:130
TAllRoutes::GetFixedRouteAt
const TOneRoute & GetFixedRouteAt(int Caller, int At) const
Returns a constant reference to the route at AllRoutesVector position 'At', after performing range ch...
Definition: TrackUnit.cpp:17500
TUtilities::clTransparent
TColor clTransparent
the display background colour, can be white, black or dark blue
Definition: Utilities.h:65
TTrainController::SignalStopWarning
bool SignalStopWarning
Definition: TrainUnit.h:775
TPrefDirElement::GetELinkPos
int GetELinkPos() const
Returns the ELink array position.
Definition: TrackUnit.h:269
TTrainController::CalcOperatingAndNotStartedTrainLateness
void CalcOperatingAndNotStartedTrainLateness(int Caller)
calculates additional lateness values for trains that haven't reached their destinations yet
Definition: TrainUnit.cpp:19462
TDisplay::PerformanceLog
void PerformanceLog(int Caller, AnsiString Statement)
Send Statement to the performance log on screen and to the file.
Definition: DisplayUnit.cpp:521
ShuttleLinkTypeForRepeatEntry
@ ShuttleLinkTypeForRepeatEntry
Definition: TrainUnit.h:80
TTrain::SignallerStopBrakeRate
double SignallerStopBrakeRate
the train brake rate when stopping under signaller control
Definition: TrainUnit.h:429
TTrainController::TLocServiceTimes::Location
AnsiString Location
Definition: TrainUnit.h:745
TOneCompleteFormattedTrain
A single train with its headcode + list of actions for use in the formatted timetable.
Definition: TrainUnit.h:261
TTrainController::SignallerTrainRemovedOnAutoSigsRoute
bool SignallerTrainRemovedOnAutoSigsRoute
true if train was on an AutoSigsRoute when removed by the signaller
Definition: TrainUnit.h:781
Terminate
@ Terminate
Definition: TrainUnit.h:50
TTrainController::MRSLow
bool MRSLow
Definition: TrainUnit.h:789
TTrack::ActiveTrackElementNameMap
TActiveTrackElementNameMap ActiveTrackElementNameMap
< map of coupled continuations
Definition: TrackUnit.h:776
TTrain::Crashed
bool Crashed
Definition: TrainUnit.h:472
TTrackElement::HLoc
int HLoc
Definition: TrackUnit.h:146
TTrain::HeadCodePosition
Graphics::TBitmap * HeadCodePosition[4]
Set from the HeadCodeGrPtr[4] pointer values, HeadCodePosition[0] is always the front,...
Definition: TrainUnit.h:486
TTrain::TrainID
int TrainID
the train's identification number
Definition: TrainUnit.h:364
Running
@ Running
Definition: TrainUnit.h:86
TAllFormattedTrains
std::vector< TTrainFormattedInformation > TAllFormattedTrains
vector of all timetabled trains for use in the formatted timetable
Definition: TrainUnit.h:283
TRailGraphics::ChangeBackgroundColour3
void ChangeBackgroundColour3(int Caller, Graphics::TBitmap *BitmapIn, Graphics::TBitmap *BitmapOut, TColor NewBackgroundColour, TColor OldBackgroundColour)
as above but uses Scanline
Definition: GraphicUnit.cpp:3575
TRailGraphics::smCyan
Graphics::TBitmap * smCyan
Definition: GraphicUnit.h:894
TTrainController::MovingSuccessor
bool MovingSuccessor(const TActionVectorEntry &AVEntry)
A shorthand function that returns true if the successor to a given timetable action command should be...
Definition: TrainUnit.cpp:13288
TTrainController::LogActionError
void LogActionError(int Caller, AnsiString HeadCode, AnsiString OtherHeadCode, TActionEventType ActionEventType, AnsiString LocationID)
Send an error message to the performance log and file, and as a warning if appropriate.
Definition: TrainUnit.cpp:14765
TTrainController::TrainVectorAt
TTrain & TrainVectorAt(int Caller, int VecPos)
Return a reference to the train at position VecPos in the TrainVector, carries out range checking on ...
Definition: TrainUnit.cpp:15522
TTrain::FrontElementSpeedLimit
int FrontElementSpeedLimit
Definition: TrainUnit.h:444
TTrack::NumberOfPlatforms
int NumberOfPlatforms(int Caller, AnsiString LocationName)
Returns the number of separate platforms (not platform elements) at a given location,...
Definition: TrackUnit.cpp:11268
TTrack::TSigElement::SpeedTag
int SpeedTag
the TrackElement SpeedTag value - specifies the signal element
Definition: TrackUnit.h:709
TTrain::DeleteTrain
void DeleteTrain(int Caller)
This is a housekeeping function to delete train heap objects (bitmaps) explicitly rather than by usin...
Definition: TrainUnit.cpp:231
TTrainController::NumFailures
int NumFailures
Definition: TrainUnit.h:832
TTrain::ExitSpeedFull
double ExitSpeedFull
speed when leaving the next element
Definition: TrainUnit.h:417
TTrain::SetHeadCodeGraphics
void SetHeadCodeGraphics(int Caller, AnsiString Code)
Set the four HeadCodeGrPtr[4] pointers to the appropriate character graphics with the current backgro...
Definition: TrainUnit.cpp:2359
TAllRoutes::SetTrailingSignalsOnAutoSigsRoute
void SetTrailingSignalsOnAutoSigsRoute(int Caller, int TrackVectorPosition, int XLinkPos)
Enter with signal at TrackVectorElement already set to red by the passing train.
Definition: TrackUnit.cpp:18515
clB5G5R5
#define clB5G5R5
Definition: GraphicUnit.h:286
TUtilities::TimeStamp
AnsiString TimeStamp()
creates a string of the form 'hh:mm:ss' for use in call & event logging
Definition: Utilities.cpp:73
TTrainController::RandomFailureCounter
unsigned int RandomFailureCounter
new at v2.4.0, resets after 53 seconds (53 prime so can trigger at any clock time)
Definition: TrainUnit.h:848
TAllRoutes::TLockedRouteClass::RouteNumber
int RouteNumber
the vector position number of the relevant route in AllRoutesVector
Definition: TrackUnit.h:1605
TTrainController::AddTrain
bool AddTrain(int Caller, int RearPosition, int FrontPosition, AnsiString HeadCode, int StartSpeed, int Mass, double MaxRunningSpeed, double MaxBrakeRate, double PowerAtRail, AnsiString ModeStr, TTrainDataEntry *TrainDataEntryPtr, int RepeatNumber, int IncrementalMinutes, int IncrementalDigits, int SignallerSpeed, bool SignallerControl, TActionEventType &EventType)
Introduce a new train to the railway, with the characteristics specified, returns true for success,...
Definition: TrainUnit.cpp:9325
TTrainController::~TTrainController
~TTrainController()
Destructor.
Definition: TrainUnit.cpp:8932
TTrainController::ControllerCheckNewServiceDepartureTime
AnsiString ControllerCheckNewServiceDepartureTime(int Caller, TActionVectorIterator Ptr, int RptNum, TTrainDataEntry *TDEPtr, TTrainDataEntry *LinkedTrainDataPtr, int IncrementalMinutes, AnsiString RetStr)
Similar to TTrain::CheckNewServiceDepartureTime for use in ContinuationEntryFloatingTTString.
Definition: TrainUnit.cpp:9832
TTrainController::TwoLocationList
TServiceCallingLocsList TwoLocationList
Definition: TrainUnit.h:842
TUtilities::CheckAndReadFileInt
bool CheckAndReadFileInt(std::ifstream &InFile, int Lowest, int Highest, int &OutInt)
checks that the value is an int lying between Lowest & Highest (inclusive), returns true for success ...
Definition: Utilities.cpp:280
TRailGraphics::Code7
Graphics::TBitmap * Code7
Definition: GraphicUnit.h:975
TTrackElement::ActiveTrackElementName
AnsiString ActiveTrackElementName
Location name used either in the timetable or for a continuation (continuation names not used in time...
Definition: TrackUnit.h:127
TRailGraphics::bm94set
Graphics::TBitmap * bm94set
Definition: GraphicUnit.h:516
TTrainController::LogEvent
void LogEvent(AnsiString Str)
store Str to the event log - moved from TUtilities for v0.6 so can record the tt clock value
Definition: TrainUnit.cpp:8943
TRailGraphics::CodeO
Graphics::TBitmap * CodeO
Definition: GraphicUnit.h:992
TTrain::ExitPair
THVShortPair ExitPair
H & V coordinates of the exit element related to TimeToExit, new for multiplayer.
Definition: TrainUnit.h:458
TAllRoutes::SignallerRemovedTrainAutoRoute
TOneRoute SignallerRemovedTrainAutoRoute
if train was on an AutoSigsRoute when removed then this stores the route so that signals can be reset
Definition: TrackUnit.h:1692
TTrainController::MassHigh
bool MassHigh
Definition: TrainUnit.h:789
TTrainController::CheckAndPopulateListOfIDs
bool CheckAndPopulateListOfIDs(int Caller, AnsiString IDSet, TNumList &ExitList, bool GiveMessages)
Used to compile ExitList from a string list of element IDs, returns true for success or gives a messa...
Definition: TrainUnit.cpp:11282
TTrain::PlotTrainWithNewBackgroundColour
void PlotTrainWithNewBackgroundColour(int Caller, TColor NewBackgroundColour, TDisplay *Disp)
Changes the train's background colour (e.g. to pale green if stopped at a station) Note that this use...
Definition: TrainUnit.cpp:3428
TUtilities::SaveFileString
void SaveFileString(std::ofstream &OutFile, AnsiString SaveString)
stores the string value to the file, then a '0' delimiter then a CR
Definition: Utilities.cpp:135
TTrain::PlotAlternativeTrackRouteGraphic
void PlotAlternativeTrackRouteGraphic(int Caller, unsigned int LagElement, int LagELinkPos, int HOffset, int VOffset, TStraddle StraddleValue)
When a train moves off a bridge the other track may contain a route or have a train on it that has be...
Definition: TrainUnit.cpp:3128
TTrain::ExitTimeFull
TDateTime ExitTimeFull
times used in SetTrainMovementValues corresponding to the next element the train runs on
Definition: TrainUnit.h:452
TTrainController::CalcDistanceToRedSignalandStopTime
int CalcDistanceToRedSignalandStopTime(int Caller, int TrackVectorPosition, int TrackVectorPositionEntryPos, bool SigControlAndCanPassRedSignal, TActionVectorEntry *AVPtr, AnsiString HeadCode, int TrainID, float &CurrentStopTime, float &LaterStopTime, float &RecoverableTime, int &AvTrackSpeed, int &DistanceToExit, THVShortPair &ExitPair)
new v2.2.0 (DistanceToExit added for multiplayer), calcs distances to red signal & exit,...
Definition: TrainUnit.cpp:19766
TTrack::GapFlashRed
TGraphicElement * GapFlashRed
the red & green circle graphics used to show where the gaps are
Definition: TrackUnit.h:784
LocTypeForRepeatEntry
@ LocTypeForRepeatEntry
Definition: TrainUnit.h:70
SignallerChangeDirection
@ SignallerChangeDirection
Definition: TrainUnit.h:52
TAllRoutes::TLockedRouteClass
Handles routes that are locked because of approaching trains.
Definition: TrackUnit.h:1603
TTrain::TrainAtLocation
bool TrainAtLocation(int Caller, AnsiString &LocationName)
True when the train is stopped at a timetabled location.
Definition: TrainUnit.cpp:8378
TTrain::IsTrainIDOnBridgeTrackPos01
bool IsTrainIDOnBridgeTrackPos01(int Caller, unsigned int TrackVectorPosition)
True if train is on a bridge on trackpos 0 & 1.
Definition: TrainUnit.cpp:2992
TActionVectorEntry
Contains a single train action in a timetable - repeat entry is also of this class though no train ac...
Definition: TrainUnit.h:120
TTrainController::TotLateExitMins
float TotLateExitMins
Definition: TrainUnit.h:808
TTrainController::CheckSessionContinuationAutoSigEntries
bool CheckSessionContinuationAutoSigEntries(int Caller, std::ifstream &SessionFile)
Part of the session file integrity check for ContinuationAutoSigEntries, true for success.
Definition: TrainUnit.cpp:15368
TOneCompleteFormattedTrain::OneFormattedTrainVector
TOneFormattedTrainVector OneFormattedTrainVector
Definition: TrainUnit.h:264
TRailGraphics::Code1
Graphics::TBitmap * Code1
Definition: GraphicUnit.h:969
TTrainController::TimetableMessage
void TimetableMessage(bool GiveMessages, AnsiString Message)
Sends a message to the user if GiveMessages is true, including ServiceReference (see above) if not nu...
Definition: TrainUnit.cpp:14732
TDisplay
Definition: DisplayUnit.h:48
TTrackElement::ConnLinkPos
int ConnLinkPos[4]
Connecting element link position (i.e. array positions of the connecting element links,...
Definition: TrackUnit.h:144
TTrainController::ContinuationAutoSigVector
TContinuationAutoSigVector ContinuationAutoSigVector
vector for TContinuationAutoSigEntry objects
Definition: TrainUnit.h:850
TTrain::StoppedWithoutPower
bool StoppedWithoutPower
Definition: TrainUnit.h:473
TTrain::NameInTimetableBeforeCDT
int NameInTimetableBeforeCDT(int Caller, AnsiString Name, bool &Stop)
Returns the number by which the train ActionVectorEntryPtr needs to be incremented to point to the lo...
Definition: TrainUnit.cpp:4561
TTrainFormattedInformation::Header
AnsiString Header
description, mass, power, brake rate etc
Definition: TrainUnit.h:276
TActionEventType
TActionEventType
Used for reporting error conditions & warnings.
Definition: TrainUnit.h:39
TTrainController::TrainExistsAtIdent
bool TrainExistsAtIdent(int Caller, int TrainID)
new at v2.4.0 return true if find the train (added at v2.4.0 as can select a removed train in OAListB...
Definition: TrainUnit.cpp:9657
SignallerJoin
@ SignallerJoin
Definition: TrainUnit.h:51
TTrain::RearStartElement
int RearStartElement
start TrackVectorPosition element for rear of train
Definition: TrainUnit.h:352
TTrain::StepForwardFlag
bool StepForwardFlag
set when the signaller command to step forward one element has been given
Definition: TrainUnit.h:401
TTrainController::SplitTrainInfo
bool SplitTrainInfo(int Caller, AnsiString TrainInfoStr, AnsiString &HeadCode, AnsiString &Description, int &StartSpeed, int &MaxRunningSpeed, int &Mass, double &MaxBrakeRate, double &PowerAtRail, int &SignallerSpeed, bool GiveMessages)
Parse a train information entry, return true for success; PowerAtRail changed to double& from int& at...
Definition: TrainUnit.cpp:11365
TTrainController::TotEarlyExitMins
float TotEarlyExitMins
Definition: TrainUnit.h:804
TFixedTrackPiece::Config
TConfiguration Config[4]
the type of link - see TConfiguration above
Definition: TrackUnit.h:96
TTrainController::Operate
void Operate(int Caller)
called every clock tick to introduce new trains and update existing trains
Definition: TrainUnit.cpp:8957
TTrackElement::TrainIDOnElement
int TrainIDOnElement
Definition: TrackUnit.h:152
TTrainController::SkippedTTEvents
int SkippedTTEvents
Definition: TrainUnit.h:827
TRailGraphics::smRed
Graphics::TBitmap * smRed
Definition: GraphicUnit.h:901
TTrain::PowerAtRail
double PowerAtRail
in Watts (taken as 80% of the train's Gross Power, i.e. that entered by the user)
Definition: TrainUnit.h:432
TRailGraphics::LCPlainMan
Graphics::TBitmap * LCPlainMan
Definition: GraphicUnit.h:745
TDisplay::GetOutputLog4
TLabel * GetOutputLog4()
Definition: DisplayUnit.h:160
TAllRoutes::RebuildRailwayFlag
bool RebuildRailwayFlag
this is set whenever a route has to be cancelled forcibly in order to force a ClearandRebuildRailway ...
Definition: TrackUnit.h:1673
TTrainDataEntry::MaxRunningSpeed
double MaxRunningSpeed
in km/h
Definition: TrainUnit.h:211
TUtilities::LoadFileInt
int LoadFileInt(std::ifstream &InFile)
loads an int value from the file
Definition: Utilities.cpp:162
TTrackElement::ElementID
AnsiString ElementID
the element identifier based on position in the railway
Definition: TrackUnit.h:129
TTimeToExitMultiMapEntry
std::pair< THVShortPair, TExitInfo > TTimeToExitMultiMapEntry
Definition: TrainUnit.h:114
TRailGraphics::Code6
Graphics::TBitmap * Code6
Definition: GraphicUnit.h:974
TTrainDataEntry::TrainOperatingDataVector
TTrainOperatingDataVector TrainOperatingDataVector
operating information for the train including all its repeats
Definition: TrainUnit.h:225
TTrackElement::VLoc
int VLoc
The h & v locations in the railway (top lh corner of the first build screen = 0,0)
Definition: TrackUnit.h:146
TTrainController::OperatingTrainLateMins
float OperatingTrainLateMins
total late minutes of operating trains on exit operation for locations not reached yet
Definition: TrainUnit.h:797
TTrack::GetNonPointsOppositeLinkPos
int GetNonPointsOppositeLinkPos(int LinkPosIn)
Return the corresponding link position (track always occupies either links 0 & 1 or 2 & 3)
Definition: TrackUnit.h:883
TTrain::UnplotTrain
void UnplotTrain(int Caller)
Unplot train from screen in zoomed-in mode.
Definition: TrainUnit.cpp:550
TTrain::IsThereAnAdjacentTrain
bool IsThereAnAdjacentTrain(int Caller, TTrain *&TrainToBeJoinedBy)
Definition: TrainUnit.cpp:5019
TTrain::TRSTime
TDateTime TRSTime
location departure time and 'train ready to start' time (TRSTime is 10 seconds before the ReleaseTime...
Definition: TrainUnit.h:454
TAllRoutes::TLockedRouteClass::LastTrackVectorPosition
unsigned int LastTrackVectorPosition
the TrackVector position of the last (i.e. most forward) element in the route
Definition: TrackUnit.h:1609
FailMissedJBO
@ FailMissedJBO
Definition: TrainUnit.h:41
TUtilities::EventLog
std::deque< AnsiString > EventLog
event store, saved to the errorlog for diagnostic purposes
Definition: Utilities.h:57
TAllRoutes::NoRoute
@ NoRoute
Definition: TrackUnit.h:1619
TActionVectorEntry::FormatType
TTimetableFormatType FormatType
defines the timetable action type
Definition: TrainUnit.h:138
TTrain::RemainHereLogNotSent
bool RemainHereLogNotSent
flag to prevent repeated logs, new at v1.2.0
Definition: TrainUnit.h:331
TRailGraphics::CodeU
Graphics::TBitmap * CodeU
Definition: GraphicUnit.h:998
TTrainController::RestartTime
TDateTime RestartTime
TTClockTime when operation pauses ( = timetable start time prior to operation) TTClockTime is calcula...
Definition: TrainUnit.h:690
TTrain::NewShuttleFromNonRepeatService
void NewShuttleFromNonRepeatService(int Caller)
Carry out the actions needed when a new shuttle service is created from a non-repeating (F-nshs) serv...
Definition: TrainUnit.cpp:6572
TTrainController::TimetableStartTime
TDateTime TimetableStartTime
the start time of the current timetable
Definition: TrainUnit.h:688
clSPADBackground
#define clSPADBackground
Definition: GraphicUnit.h:300
TTrainController::UnplotTrains
void UnplotTrains(int Caller)
unplot all trains from screen
Definition: TrainUnit.cpp:9309
TRailGraphics::smBrightGreen
Graphics::TBitmap * smBrightGreen
Definition: GraphicUnit.h:892
TTrainController::EarlyArrivals
int EarlyArrivals
Definition: TrainUnit.h:813
FailCreateTrain
@ FailCreateTrain
Definition: TrainUnit.h:40
TTrain::IncrementalDigits
int IncrementalDigits
the number of digits to increment by in repeat entries
Definition: TrainUnit.h:348
TTrainController::CreateFormattedTimetable
void CreateFormattedTimetable(int Caller, AnsiString RailwayTitle, AnsiString TimetableTitle, AnsiString CurDir)
Examines the internal timetable (TrainDataVector) and creates from it a chronological (....
Definition: TrainUnit.cpp:15535
TTrack::RouteFlashFlag
bool RouteFlashFlag
true while a route is flashing prior to being set
Definition: TrackUnit.h:748
TRailGraphics::Code_v
Graphics::TBitmap * Code_v
Definition: GraphicUnit.h:963
TTrackElement::StationEntryStopLinkPos1
int StationEntryStopLinkPos1
Definition: TrackUnit.h:150
Points
@ Points
Definition: TrackUnit.h:65
TTrainController::SplitEntry
bool SplitEntry(int Caller, AnsiString OneEntry, bool GiveMessages, bool CheckLocationsExistInRailway, AnsiString &First, AnsiString &Second, AnsiString &Third, AnsiString &Fourth, int &RearStartOrRepeatMins, int &FrontStartPosition, TTimetableFormatType &TimetableFormatType, TTimetableLocationType &LocationType, TTimetableSequenceType &SequenceType, TTimetableShuttleLinkType &ShuttleLinkType, TNumList &ExitList, bool &Warning)
Parse a single timetable service action, return true for success.
Definition: TrainUnit.cpp:10874
TRailGraphics::LCPlain
Graphics::TBitmap * LCPlain
Definition: GraphicUnit.h:738
TTrain::LastActionDelayFlag
bool LastActionDelayFlag
used when trains join to ensure that there is a 30 second delay before the actual join takes place af...
Definition: TrainUnit.h:389
TTrainController::TotLatePassMins
float TotLatePassMins
Definition: TrainUnit.h:807
TTrainController::StripSpaces
void StripSpaces(int Caller, AnsiString &Input)
Strip both leading and trailing spaces at ends of Input and spaces before and after all commas and se...
Definition: TrainUnit.cpp:13845
FailMissedJoinOther
@ FailMissedJoinOther
Definition: TrainUnit.h:41
TTrain::RepeatShuttleOrRemainHere
void RepeatShuttleOrRemainHere(int Caller)
Carry out the actions needed to create either a new shuttle service or (if all repeats have finished)...
Definition: TrainUnit.cpp:6613
TTrain::JoinedOtherTrainFlag
bool JoinedOtherTrainFlag
true when the train has joined another train following an 'Fjo' timetable command or a signaller join...
Definition: TrainUnit.h:387
TTrainController::BFLow
bool BFLow
Definition: TrainUnit.h:789
TTrainController::TotArrDepPass
int TotArrDepPass
Definition: TrainUnit.h:830
SignallerPassRedSignal
@ SignallerPassRedSignal
Definition: TrainUnit.h:52
TRailGraphics::ChangeForegroundColour
void ChangeForegroundColour(int Caller, Graphics::TBitmap *BitmapIn, Graphics::TBitmap *BitmapOut, TColor NewForegroundColour, TColor BackgroundColour)
Definition: GraphicUnit.cpp:3423
TRailGraphics::CodeE
Graphics::TBitmap * CodeE
Definition: GraphicUnit.h:982
FailLockedRoute
@ FailLockedRoute
Definition: TrainUnit.h:40
TTrain::SignallerRemoved
bool SignallerRemoved
set when removed under signaller control to force a removal from the display at the next clock tick
Definition: TrainUnit.h:395
clB5G3R0
#define clB5G3R0
Definition: GraphicUnit.h:267
TTrain::FrontCodePtr
Graphics::TBitmap * FrontCodePtr
points to the front headcode segment, this is set to red or blue depending on TrainMode
Definition: TrainUnit.h:490
TOneRoute::SetRouteSignals
void SetRouteSignals(int Caller) const
Called when setting a route to set all points appropriately. Also called when a new train is added at...
Definition: TrackUnit.cpp:16761
Continuation
@ Continuation
Definition: TrackUnit.h:65
TTrainController::CallOnWarning
bool CallOnWarning
Definition: TrainUnit.h:775
TTrack::GetFilletGraphic
Graphics::TBitmap * GetFilletGraphic(int Caller, TTrackElement TrackElement)
Return a pointer to the point fillet (the bit that appears to move when points are changed) for the p...
Definition: TrackUnit.cpp:7526
GraphicUnit.h
TTrack::PointFlashFlag
bool PointFlashFlag
true when points are flashing during manual change
Definition: TrackUnit.h:746
TTrainController::OnTimeDeps
int OnTimeDeps
Definition: TrainUnit.h:823
TTrain::RestoreTimetableLocation
AnsiString RestoreTimetableLocation
stores the location name at which signaller control is taken, to ensure that it is back at that locat...
Definition: TrainUnit.h:464
TTrainController::TimetableIntegrityCheck
bool TimetableIntegrityCheck(int Caller, char *FileName, bool GiveMessages, bool CheckLocationsExistInRailway)
Checks overall timetable integrity, calls many other specific checking functions, returns true for su...
Definition: TrainUnit.cpp:10011
AllRoutes
TAllRoutes * AllRoutes
the object pointer, object created in InterfaceUnit
Definition: TrackUnit.cpp:54
TTrain::SignallerMaxSpeed
int SignallerMaxSpeed
maximum train speed under signaller control (in km/h)
Definition: TrainUnit.h:358
NoEvent
@ NoEvent
Definition: TrainUnit.h:40
FailSplitDueToOtherTrain
@ FailSplitDueToOtherTrain
Definition: TrainUnit.h:40
TTrainController::SendPerformanceSummary
void SendPerformanceSummary(int Caller, std::ofstream &PerfFile)
At the end of operation a summary of overall performance is sent to the performance file by this func...
Definition: TrainUnit.cpp:18989
TTrain::ResetTrainElementID
void ResetTrainElementID(int Caller, unsigned int TrackVectorPosition, int EntryPos)
After a train has moved off an element that element has its TrainIDOnElement value set back to -1 to ...
Definition: TrainUnit.cpp:3086
TRailGraphics::smMagenta
Graphics::TBitmap * smMagenta
Definition: GraphicUnit.h:897
RepairFailedTrain
@ RepairFailedTrain
Definition: TrainUnit.h:52
TTrainController::AllServiceCallingLocsMap
TAllServiceCallingLocsMap AllServiceCallingLocsMap
Definition: TrainUnit.h:758
TRailGraphics::smPaleGreen
Graphics::TBitmap * smPaleGreen
Definition: GraphicUnit.h:900
TRailGraphics::CodeS
Graphics::TBitmap * CodeS
Definition: GraphicUnit.h:996
TTrainController::ContinuationEntryFloatingTTString
AnsiString ContinuationEntryFloatingTTString(int Caller, TTrainDataEntry *TTDEPtr, int RepeatNumber, int IncrementalMinutes, int IncrementalDigits)
Build string for use in floating window for expected trains at continuations.
Definition: TrainUnit.cpp:9688
TFixedTrackPiece::SpeedTag
int SpeedTag
The element identification number - corresponds to the relevant SpeedButton->Tag.
Definition: TrackUnit.h:87
TTrain::SPADFlag
bool SPADFlag
set when running past a red signal without permission flags to indicate relevant stop conditions or p...
Definition: TrainUnit.h:470
TTrain::Stopped
bool Stopped()
True if the train has stopped for any reason.
Definition: TrainUnit.h:657
TTrack::SigTableGroundSignal
TSigElement SigTableGroundSignal[40]
new at version 0.6 for ground signals
Definition: TrackUnit.h:723
TTrain::TrainToJoinIsAdjacent
bool TrainToJoinIsAdjacent(int Caller, TTrain *&TrainToJoin)
True for a train waiting to join another when the other train is adjacent.
Definition: TrainUnit.cpp:6515
TActionVectorEntry::ExitList
TNumList ExitList
the list of valid train exit TrackVector positions for 'Fer' entries (empty to begin with)
Definition: TrainUnit.h:136
TTrainController::OnTimeExits
int OnTimeExits
Definition: TrainUnit.h:825
TTrain::OldZoomOutElement
int OldZoomOutElement[3]
stores the Lead, Mid & Lag TrackVectorPositions, used for unplotting trains from the old position in ...
Definition: TrainUnit.h:477
TPrefDirElement::GetELink
int GetELink() const
Returns ELink.
Definition: TrackUnit.h:263
TDisplay::GetOutputLog8
TLabel * GetOutputLog8()
Definition: DisplayUnit.h:180
WaitingForJBO
@ WaitingForJBO
Definition: TrainUnit.h:43
TUtilities::CheckFileDouble
bool CheckFileDouble(std::ifstream &InFile)
checks that the value is a double, returns true for success
Definition: Utilities.cpp:331
TTrain::Straddle
TStraddle Straddle
the current Straddle value of the train (see TStraddle above)
Definition: TrainUnit.h:497
SignallerStop
@ SignallerStop
Definition: TrainUnit.h:52
TRailGraphics::CodeB
Graphics::TBitmap * CodeB
Definition: GraphicUnit.h:979
TAllRoutes::DiagonalFouledByRouteOrTrain
bool DiagonalFouledByRouteOrTrain(int Caller, int HLoc, int VLoc, int DiagonalLinkNumber)
The track geometry allows diagonals to cross without occupying the same track element,...
Definition: TrackUnit.cpp:19179
TTrackElement::TempTrackMarker01
bool TempTrackMarker01
Definition: TrackUnit.h:138
Display
TDisplay * Display
The object pointer for the on-screen display, object created in InterfaceUnit.
Definition: DisplayUnit.cpp:53
TTrainController::GetServiceFromVector
TTrainDataEntry GetServiceFromVector(AnsiString Caller, AnsiString ServiceReference, TTrainDataVector Vector, bool &FinishType, bool &FoundFlag)
Definition: TrainUnit.cpp:18243
TTrack::GetVLocMin
int GetVLocMin()
Definition: TrackUnit.h:877
TRailGraphics::Code3
Graphics::TBitmap * Code3
Definition: GraphicUnit.h:971
TUtilities::CheckFileBool
bool CheckFileBool(std::ifstream &InFile)
checks that the value is a bool returns true for success
Definition: Utilities.cpp:209
TTrack::OtherTrainOnTrack
bool OtherTrainOnTrack(int Caller, int NextPos, int NextEntryPos, int OwnTrainID)
True if another train on NextEntryPos track of element at NextPos, whether bridge or not,...
Definition: TrackUnit.cpp:10906
TTrain::MaximumPowerLimit
static const int MaximumPowerLimit
Watts (i.e. 100MW)
Definition: TrainUnit.h:313
TTrain::PlotStartPosition
void PlotStartPosition(int Caller)
Plots the train and sets up all relevant members for a new train when it is introduced into the railw...
Definition: TrainUnit.cpp:277
TTrack::TActiveTrackElementNameMapEntry
std::pair< AnsiString, int > TActiveTrackElementNameMapEntry
Definition: TrackUnit.h:702
TTrainController::SingleServiceOutput
void SingleServiceOutput(int Caller, int SSVectorNumber, TNumList MarkerList, TTrainDataVector &SingleServiceVector, std::ofstream &VecFile)
Outputs the single service vector for train direction analysis purposes in timetable conflict analysi...
Definition: TrainUnit.cpp:18136
TTrainController::TContinuationAutoSigEntry::SecondDelay
double SecondDelay
Definition: TrainUnit.h:699
TTrain::UnplotTrainInZoomOutMode
void UnplotTrainInZoomOutMode(int Caller)
Unplot train from screen in zoomed-out mode.
Definition: TrainUnit.cpp:8341
TTimetableFormatType
TTimetableFormatType
Timetable entry types.
Definition: TrainUnit.h:63
TTrainController::TrainFailedWarning
bool TrainFailedWarning
Flags to enable the relevant warning graphics to flash at the left hand side of the screen.
Definition: TrainUnit.h:775
TTrain::NextTrainID
static int NextTrainID
the ID value to be used for the next train that is created, static so that it doesn't need an object ...
Definition: TrainUnit.h:318
TTrainController::TLocServiceTimes
Class used for timetable conflict file compilation.
Definition: TrainUnit.h:744
TTrainOperatingData::RunningEntry
TRunningEntry RunningEntry
Definition: TrainUnit.h:185
NotAShuttleLink
@ NotAShuttleLink
Definition: TrainUnit.h:80
TRailGraphics::gl91set
Graphics::TBitmap * gl91set
Definition: GraphicUnit.h:721
TRailGraphics::Code_y
Graphics::TBitmap * Code_y
Definition: GraphicUnit.h:966
TAllRoutes::CheckMapAndRoutes
void CheckMapAndRoutes(int Caller)
Diagnostic function - checks equivalence for each route between entries in PrefDirVector and those in...
Definition: TrackUnit.cpp:18288
TAllRoutes::GetRouteElementDataFromRoute2MultiMap
TRouteElementPair GetRouteElementDataFromRoute2MultiMap(int Caller, int HLoc, int VLoc, TRouteElementPair &SecondPair)
Retrieve up to two TRouteElementPair entries from Route2MultiMap at H & V, the first as a function re...
Definition: TrackUnit.cpp:18244
TTrainController::TrainAdded
bool TrainAdded
true when a train has been added by a split (occurs outside the normal train introduction process)
Definition: TrainUnit.h:779
TActionVectorEntry::LocationName
AnsiString LocationName
Definition: TrainUnit.h:122
TTrainFormattedInformation::OneCompleteFormattedTrainVector
TOneCompleteFormattedTrainVector OneCompleteFormattedTrainVector
Definition: TrainUnit.h:280
ShuttleFinishedRemainingHere
@ ShuttleFinishedRemainingHere
Definition: TrainUnit.h:43
FailUnexpectedBuffers
@ FailUnexpectedBuffers
Definition: TrainUnit.h:41
Start
@ Start
Definition: TrainUnit.h:75
TRailGraphics::TempBackground
Graphics::TBitmap * TempBackground
Definition: GraphicUnit.h:904
TTrain::TimeTimeLocArrived
bool TimeTimeLocArrived
indicates whether has arrived (true) or not when ActionVectorEntryPtr->FormatType == TimeTimeLoc
Definition: TrainUnit.h:329
TActionType
TActionType
Used in LogAction when reporting a train action to the performance log & file.
Definition: TrainUnit.h:49
TTrainController::TContinuationAutoSigEntry::ThirdDelay
double ThirdDelay
Delays in seconds before consecutive signal changes - these correspond to the times taken for trains ...
Definition: TrainUnit.h:699
TTrain::LeadEntryPos
int LeadEntryPos
Definition: TrainUnit.h:362
TTrainController::LateArrivals
int LateArrivals
Definition: TrainUnit.h:817
TRailGraphics::CodeZ
Graphics::TBitmap * CodeZ
Definition: GraphicUnit.h:1003
NoShuttleLink
@ NoShuttleLink
Definition: TrainUnit.h:80
TTrainController::ReplotTrains
void ReplotTrains(int Caller, TDisplay *Disp)
plot all trains on the display
Definition: TrainUnit.cpp:9279
TTrainController::CheckShuttleServiceIntegrity
bool CheckShuttleServiceIntegrity(int Caller, TTrainDataEntry *TDEntryPtr, bool GiveMessages)
Check that each shuttle service ends either in Fns or Fxx-sh (though a single service can't end in Fx...
Definition: TrainUnit.cpp:14679
TTrain::FloatingTimetableString
AnsiString FloatingTimetableString(int Caller, TActionVectorEntry *Ptr)
Used in the floating window to display the timetable.
Definition: TrainUnit.cpp:7089
TAllRoutes::GetModifiableRouteAt
TOneRoute & GetModifiableRouteAt(int Caller, int At)
Returns a modifiable reference to the route at AllRoutesVector position 'At', after performing range ...
Definition: TrackUnit.cpp:17514
TTrain::AValue
double AValue
this is a useful shorthand value in calculating speeds and transit times in SetTrainMovementValues [=...
Definition: TrainUnit.h:409
FailUnexpectedExitRailway
@ FailUnexpectedExitRailway
Definition: TrainUnit.h:41
TTrain::FrontTrainSplit
void FrontTrainSplit(int Caller)
Carry out the actions needed when a train is to split from the front.
Definition: TrainUnit.cpp:5459
TTrainController::WithinTimeRange
bool WithinTimeRange(int Caller, AnsiString Time1, AnsiString Time2, int MinuteRange)
check whether the two times are within the range in minutes specified and return true if so....
Definition: TrainUnit.cpp:18279
TTrainController::WriteTrainsToImage
void WriteTrainsToImage(int Caller, Graphics::TBitmap *Bitmap)
Called by TInterface::SaveOperatingImage1Click) to write all trains to the image file.
Definition: TrainUnit.cpp:9294
TActionVectorEntry::LinkedTrainEntryPtr
TTrainDataEntry * LinkedTrainEntryPtr
link pointer for use between fsp/rsp & Sfs; Fjo & jbo; Fns & Sns; & all shuttle to shuttle links
Definition: TrainUnit.h:146
TTrainController::AtLocSuccessor
bool AtLocSuccessor(const TActionVectorEntry &AVEntry)
A shorthand function that returns true if the successor to a given timetable action command should be...
Definition: TrainUnit.cpp:13295
TTrain::ZeroPowerNoJoinedByMessage
bool ZeroPowerNoJoinedByMessage
Definition: TrainUnit.h:338
TTrainController::SaveSessionContinuationAutoSigEntries
void SaveSessionContinuationAutoSigEntries(int Caller, std::ofstream &SessionFile)
save ContinuationAutoSigEntries to a session file
Definition: TrainUnit.cpp:15328
TTrainOperatingData::TrainID
int TrainID
Definition: TrainUnit.h:183
TDisplay::GetOutputLog3
TLabel * GetOutputLog3()
Definition: DisplayUnit.h:155
NoLocation
@ NoLocation
Definition: TrainUnit.h:70
TRailGraphics::bmTransparentBgnd
Graphics::TBitmap * bmTransparentBgnd
Definition: GraphicUnit.h:914
TTrain::FirstHalfMove
bool FirstHalfMove
true when the train is on the first half of an element when it displays as fully on two elements....
Definition: TrainUnit.h:385
FailLocTooShort
@ FailLocTooShort
Definition: TrainUnit.h:40
TTrainDataEntry::Mass
int Mass
in kg
Definition: TrainUnit.h:215
NoMode
@ NoMode
Definition: TrainUnit.h:58
TTrainController::LatePasses
int LatePasses
Definition: TrainUnit.h:819
TTrainController::SplitRepeat
bool SplitRepeat(int Caller, AnsiString OneEntry, int &RearStartOrRepeatMins, int &FrontStartOrRepeatDigits, int &RepeatNumber, bool GiveMessages)
Parse a timetable repeat entry, return true for success.
Definition: TrainUnit.cpp:11713
TFixedTrackPiece::TrackType
TTrackType TrackType
the type of track element
Definition: TrackUnit.h:99
TTrainController::TLocServiceTimes::ArrTime
AnsiString ArrTime
Definition: TrainUnit.h:748
TTrainController::SSHigh
bool SSHigh
Definition: TrainUnit.h:789
TimeLoc
@ TimeLoc
Definition: TrainUnit.h:64
TActionVectorIterator
TActionVector::iterator TActionVectorIterator
iterator
Definition: TrainUnit.h:174
TPrefDirElement
Basic preferred direction or route element - track element with additional members.
Definition: TrackUnit.h:195
TTrainController::AvHoursIntValue
int AvHoursIntValue
Input in MTBFEditBox in timetable hours, min value is 1 and max is 10,000. Here because performance f...
Definition: TrainUnit.h:840
TTrainController::CrashedTrains
int CrashedTrains
Definition: TrainUnit.h:811
TTrainController::TTEditPanelVisible
bool TTEditPanelVisible
new at v2.6.0 so potential error message only shows in TTEdit mode
Definition: TrainUnit.h:787
TDisplay::WarningLog
void WarningLog(int Caller, AnsiString Statement)
Display warning message Statement in the bottom left hand warning position and scroll other messages ...
Definition: DisplayUnit.cpp:531
TTrain::LogAction
void LogAction(int Caller, AnsiString HeadCode, AnsiString OtherHeadCode, TActionType ActionType, AnsiString LocationName, TDateTime TimetableNonRepeatTime, bool Warning)
Send a message to the performance log and performance file, and if the message is flagged as a warnin...
Definition: TrainUnit.cpp:5103
TTrain::StoppedAtSignal
bool StoppedAtSignal
Definition: TrainUnit.h:472
TTrainController::GetControllerTrainTime
TDateTime GetControllerTrainTime(int Caller, TDateTime Time, int RepeatNumber, int IncrementalMinutes)
Get the interval between repeats.
Definition: TrainUnit.cpp:9676
TRailGraphics::Code_u
Graphics::TBitmap * Code_u
Definition: GraphicUnit.h:962
TTrackElement::Attribute
int Attribute
special variable used only for points, signals & level crossings, ignored otherwise; points 0=set to ...
Definition: TrackUnit.h:140
TStraddle
TStraddle
Defines the train position with respect to the track elements; three consecutive elements are Lead (f...
Definition: TrainUnit.h:295
FailEntryRouteSetAgainst
@ FailEntryRouteSetAgainst
Definition: TrainUnit.h:44
TTrain::TrainSkippedEvents
int TrainSkippedEvents
stores the pointer increment from the current action in ActionVector for skipped actions when a depar...
Definition: TrainUnit.h:373
TTrainController::THCandTrainPosParam
std::pair< AnsiString, int > THCandTrainPosParam
Definition: TrainUnit.h:760
TExitInfo::ServiceReference
AnsiString ServiceReference
Definition: TrainUnit.h:106
TTrainDataVector
std::vector< TTrainDataEntry > TTrainDataVector
vector class for containing the whole timetable - one entry per timetable service entry (the object i...
Definition: TrainUnit.h:238
TTrainController::GetRepeatTime
TDateTime GetRepeatTime(int Caller, TDateTime BasicTime, int RepeatNumber, int IncMinutes)
Return the repeating service time.
Definition: TrainUnit.cpp:14359
clCrashedBackground
#define clCrashedBackground
Definition: GraphicUnit.h:293
TTrack::IsLCAtHV
bool IsLCAtHV(int Caller, int HLoc, int VLoc)
True if a level crossing is found at H & V.
Definition: TrackUnit.cpp:7265
TTrain::OpTimeToAct
float OpTimeToAct
in minutes: new at v2.2.0 for operator time to act panel. Calculated in UpdateTrain,...
Definition: TrainUnit.h:438
TTrainController::TTClockTime
TDateTime TTClockTime
the time indicated by the timetable clock
Definition: TrainUnit.h:686
TAllRoutes::TLockedRouteClass::LockStartTime
TDateTime LockStartTime
the timetable time at which the route is locked, to start the 2 minute clock
Definition: TrackUnit.h:1613
TActionVectorEntry::SequenceType
TTimetableSequenceType SequenceType
indicates where in the sequence of codes the action lies
Definition: TrainUnit.h:142
TTrain::RearStartExitPos
int RearStartExitPos
the LinkPos value for the rear starting element (i.e. links to the front starting element)
Definition: TrainUnit.h:354
TTrainController::TLocServiceTimes::FrhMarker
AnsiString FrhMarker
Definition: TrainUnit.h:750
TRailGraphics::Code_o
Graphics::TBitmap * Code_o
Definition: GraphicUnit.h:956
TRailGraphics::CodeV
Graphics::TBitmap * CodeV
Definition: GraphicUnit.h:999
TTrainController::TLocServiceTimes::ServiceAndRepeatNum
AnsiString ServiceAndRepeatNum
Definition: TrainUnit.h:746
TTrain::SetTrainElementID
void SetTrainElementID(int Caller, unsigned int TrackVectorPosition, int EntryPos)
When a train moves onto an element that element has its TrainIDOnElement value set to the TrainID val...
Definition: TrainUnit.cpp:3050
TActionVectorEntry::NonRepeatingShuttleLinkHeadCode
AnsiString NonRepeatingShuttleLinkHeadCode
string values for timetabled event entries, null on creation
Definition: TrainUnit.h:122
TActionVectorEntry::ShuttleLinkType
TTimetableShuttleLinkType ShuttleLinkType
indicates whether or not the action relates to a shuttle service link
Definition: TrainUnit.h:144
TTrain::TimetableFinished
bool TimetableFinished
set when there are no more timetable actions
Definition: TrainUnit.h:405
TUtilities::CallLog
std::deque< AnsiString > CallLog
call stack store, saved to the errorlog for diagnostic purposes
Definition: Utilities.h:55
TRailGraphics::CodeN
Graphics::TBitmap * CodeN
Definition: GraphicUnit.h:991
TActionVectorEntry::Command
AnsiString Command
Definition: TrainUnit.h:122
TTrain::CallingOnAllowed
bool CallingOnAllowed(int Caller)
True if the train can be called on at its current position - see detail in .cpp file.
Definition: TrainUnit.cpp:4728
TTrainController::TContinuationTrainExpectationEntry::HeadCode
AnsiString HeadCode
service headcode
Definition: TrainUnit.h:719
TTrain::RepeatShuttleOrNewNonRepeatService
void RepeatShuttleOrNewNonRepeatService(int Caller)
Carry out the actions needed to create either a new shuttle service or (if all repeats have finished)...
Definition: TrainUnit.cpp:6675
TTrackElement::Length23
int Length23
Definition: TrackUnit.h:148
TTrain::SignallerStoppingFlag
bool SignallerStoppingFlag
set when the signaller stop command has been given
Definition: TrainUnit.h:397
TTrainController::NotStartedTrainLateArr
int NotStartedTrainLateArr
total number of arrivals & departures for trains that haven't started yet for locations not reached y...
Definition: TrainUnit.h:836
TUtilities::LoadFileBool
bool LoadFileBool(std::ifstream &InFile)
loads a bool value from the file
Definition: Utilities.cpp:145
TTrainController::SigSLow
bool SigSLow
Message flags in TT checks to stop being given twice.
Definition: TrainUnit.h:789
TRailGraphics::Code_m
Graphics::TBitmap * Code_m
Definition: GraphicUnit.h:954
TTrack::AnyLinkedBarrierDownVectorManual
bool AnyLinkedBarrierDownVectorManual(int Caller, int HLoc, int VLoc, int &BDVectorPos)
Checks BarrierDownVector and returns true if there is one that is linked to the LC at H & V positions...
Definition: TrackUnit.cpp:6289
TRailGraphics::gl92set
Graphics::TBitmap * gl92set
Definition: GraphicUnit.h:723
SNSShuttle
@ SNSShuttle
Definition: TrainUnit.h:64
RemoveTrain
@ RemoveTrain
Definition: TrainUnit.h:51
TTrainController::TLocServiceTimes::DepTime
AnsiString DepTime
Definition: TrainUnit.h:749
TTrainController::SetWarningFlags
void SetWarningFlags(int Caller)
This sets all the warning flags (CrashWarning, DerailWarning etc) to their required states after a se...
Definition: TrainUnit.cpp:19416
TDisplay::PlotSmallOutput
void PlotSmallOutput(int Caller, int HPos, int VPos, Graphics::TBitmap *PlotItem)
Plot small (4x4) graphic PlotItem on the zoomed-out display at HPos & Vpos.
Definition: DisplayUnit.cpp:113
TTrainController::TContinuationTrainExpectationEntry
Class that stores data for trains expected at continuation entries (kept in a multimap - see below),...
Definition: TrainUnit.h:715
SNSNonRepeatFromShuttle
@ SNSNonRepeatFromShuttle
Definition: TrainUnit.h:64
TTrainController::TotEarlyPassMins
float TotEarlyPassMins
Definition: TrainUnit.h:803
TRailGraphics::CodeH
Graphics::TBitmap * CodeH
Definition: GraphicUnit.h:985
TTrainController::OpTimeToActMultiMap
TOpTimeToActMultiMap OpTimeToActMultiMap
added v2.2.0 for Op time to act display
Definition: TrainUnit.h:854
TTrainController::TContinuationAutoSigEntry::AccessNumber
int AccessNumber
the number of times the signal changing function has been accessed - starts at 0 and increments after...
Definition: TrainUnit.h:701
TActionVectorEntry::SignallerControl
bool SignallerControl
indicates a train that is defined by the timetable as under signaller control
Definition: TrainUnit.h:126
TTrain::GetTrainTime
TDateTime GetTrainTime(int Caller, TDateTime Time)
Returns the timetable action time corresponding to 'Time' for this train, i.e. it adjusts the time va...
Definition: TrainUnit.cpp:5008
TTrain::BrakeRate
double BrakeRate
the current train brake rate
Definition: TrainUnit.h:427
TTrackElement::SpeedLimit23
int SpeedLimit23
Element lengths and speed limits, ...01 is for the track with link positions [0] and [1],...
Definition: TrackUnit.h:148
TTrainController::TContinuationAutoSigEntry::PassoutTime
TDateTime PassoutTime
the timetable clock time at which the train exits from the continuation
Definition: TrainUnit.h:705
TTrain::FinishJoinLogSent
bool FinishJoinLogSent
Definition: TrainUnit.h:333
TTrack::GapFlashFlag
bool GapFlashFlag
true when a pair of connected gaps is flashing
Definition: TrackUnit.h:734
TRailGraphics::gl95set
Graphics::TBitmap * gl95set
Definition: GraphicUnit.h:727
TrainUnit.h
PassTime
@ PassTime
Definition: TrainUnit.h:65
TTrainController::IncorrectExits
int IncorrectExits
Definition: TrainUnit.h:816
TTrain::ContinuationExit
bool ContinuationExit(int Caller, int Element, int Exitpos) const
True if Element is a continuation and Exitpos is the continuation end.
Definition: TrainUnit.cpp:2974
TFixedTrackPiece::Link
int Link[4]
Track connection link values, max. of 4, unused = -1, top lh diag.=1, top=2, top rh diag....
Definition: TrackUnit.h:89
TTrain::UpdateTrain
void UpdateTrain(int Caller)
Major function called at each clock tick for each train & handles all train movement & associated act...
Definition: TrainUnit.cpp:642
TRailGraphics::LockedRouteCancelPtr
Graphics::TBitmap * LockedRouteCancelPtr[10]
for locked route cancel graphic, 1 for each of 8 links, 0 & 5 included as for direction
Definition: GraphicUnit.h:1052
TRailGraphics::Code8
Graphics::TBitmap * Code8
Definition: GraphicUnit.h:976
TTrainController::SameDirection
bool SameDirection(int Caller, AnsiString Ref1, AnsiString Ref2, AnsiString Time1, AnsiString Time2, int RepeatNum1, int RepeatNum2, TServiceCallingLocsList List1, TServiceCallingLocsList List2, AnsiString Location, bool Arrival)
Determines whether two services are running in the same direction when they arrive or depart from Loc...
Definition: TrainUnit.cpp:18563
TTrain::TrainToBeJoinedByIsAdjacent
bool TrainToBeJoinedByIsAdjacent(int Caller, TTrain *&TrainToBeJoinedBy)
True for a train waiting to be joined when the joining train is adjacent.
Definition: TrainUnit.cpp:6543
TTrain::SetTrainMovementValues
void SetTrainMovementValues(int Caller, int TrackVectorPosition, int EntryPos)
Calculates train speeds and times for the element that the train is about to enter....
Definition: TrainUnit.cpp:3470
TActionVectorEntry::Warning
bool Warning
if set triggers an alert in the warning panel when the action is reached
Definition: TrainUnit.h:128
TTrain::PickUpBackgroundBitmap
void PickUpBackgroundBitmap(int Caller, int HOffset, int VOffset, int Element, int EntryPos, Graphics::TBitmap *GraphicPtr) const
Store the background bitmap pointer (BackgroundPtr - see above) prior to being overwritten by the tra...
Definition: TrainUnit.cpp:2562
clStoppedTrainInFront
#define clStoppedTrainInFront
Definition: GraphicUnit.h:302
TDisplay::GetOutputLog10
TLabel * GetOutputLog10()
Definition: DisplayUnit.h:190
TTrainDataEntry::ServiceReference
AnsiString ServiceReference
Definition: TrainUnit.h:207
TTrain::SignallerChangeTrainDirection
void SignallerChangeTrainDirection(int Caller)
Unplots & replots train, which checks for facing signal and sets StoppedAtSignal if req'd.
Definition: TrainUnit.cpp:6856
TTrain::ZeroPowerNoRearSplitMessage
bool ZeroPowerNoRearSplitMessage
Definition: TrainUnit.h:336
FailMissedChangeDirection
@ FailMissedChangeDirection
Definition: TrainUnit.h:42
NotSet
@ NotSet
Definition: TrackUnit.h:75
Repeat
@ Repeat
Definition: TrainUnit.h:65
TRailGraphics::Code_c
Graphics::TBitmap * Code_c
Definition: GraphicUnit.h:944
TTrainController::StripExcessFromHeadCode
void StripExcessFromHeadCode(int Caller, AnsiString &HeadCode)
change an extended headcode to an ordinary 4 character headcode
Definition: TrainUnit.cpp:13304
TTrack::TIMPair
std::pair< unsigned int, unsigned int > TIMPair
TrackElement pair type used for inactive elements, values are vector positions.
Definition: TrackUnit.h:670
TUtilities::Clock2Stopped
bool Clock2Stopped
when true the main loop - Interface->ClockTimer2 - is stopped
Definition: Utilities.h:38
TTrainController::MRSHigh
bool MRSHigh
Definition: TrainUnit.h:789
TTrackElement::GroundSignal
@ GroundSignal
Definition: TrackUnit.h:157
LevelCrossing
@ LevelCrossing
Definition: TrackUnit.h:66
TDisplay::GetOutputLog2
TLabel * GetOutputLog2()
Definition: DisplayUnit.h:150
TTrain::NotInService
bool NotInService
Definition: TrainUnit.h:473
TRailGraphics::CodeA
Graphics::TBitmap * CodeA
Definition: GraphicUnit.h:978
TTrack::ActiveTrackElementNameMapCompiledFlag
bool ActiveTrackElementNameMapCompiledFlag
indicates that the ActiveTrackElementNameMap has been compiled
Definition: TrackUnit.h:728
TTrainController::LocServiceTimesLocationSort
bool LocServiceTimesLocationSort(TLocServiceTimes i, TLocServiceTimes j)
Definition: TrainUnit.h:865
TOneTrainFormattedEntry
A single train timetable action for use in a formatted timetable.
Definition: TrainUnit.h:246
TTrain::RearTrainSplit
void RearTrainSplit(int Caller)
Carry out the actions needed when a train is to split from the rear.
Definition: TrainUnit.cpp:5769
TTrainController::ConsolidateSARNTArrDep
AnsiString ConsolidateSARNTArrDep(int Caller, const AnsiString Input, int &NumTrainsAtLoc, AnsiString Location, bool Arrival, bool &AnalysisError, int &MaxNumberOfSameDirections)
Removes duplicates from and sorts ServiceAndRepeatNumTotal into alphabetical order for arrivals (bool...
Definition: TrainUnit.cpp:18305
Track
TTrack * Track
the object pointer, object created in InterfaceUnit
Definition: TrackUnit.cpp:53
TTrackElement::Conn
int Conn[4]
Connecting element position in TrackVector, set to -1 if no connecting link or if track not linked.
Definition: TrackUnit.h:142
Signaller
@ Signaller
Definition: TrainUnit.h:58
TTrain::TrainCrashedInto
int TrainCrashedInto
the TrainID of the train that this train has crashed into, recorded so that train can be marked and d...
Definition: TrainUnit.h:483
TRailGraphics::smYellow
Graphics::TBitmap * smYellow
Definition: GraphicUnit.h:902
RailGraphics
TRailGraphics * RailGraphics
the object pointer, object created in InterfaceUnit
Definition: GraphicUnit.cpp:50
TTrainOperatingData
Data for a specific train for use during operation.
Definition: TrainUnit.h:181
FailCreatePoints
@ FailCreatePoints
Definition: TrainUnit.h:40
TRailGraphics::smBlack
Graphics::TBitmap * smBlack
Definition: GraphicUnit.h:890
Bridge
@ Bridge
Definition: TrackUnit.h:65
TRailGraphics::Code_p
Graphics::TBitmap * Code_p
Definition: GraphicUnit.h:957
TTrainFormattedInformation::NumberOfTrains
int NumberOfTrains
number of repeats + 1
Definition: TrainUnit.h:278
TTrain::LagExitPos
int LagExitPos
TrackVector positions, & entry & exit connection positions for the elements that the train occupies.
Definition: TrainUnit.h:362
TTimetableSequenceType
TTimetableSequenceType
Definition: TrainUnit.h:74
TTrain::TimetableMaxRunningSpeed
double TimetableMaxRunningSpeed
the maximum train running speed when in timetable mode (see int SignallerMaxSpeed for signaller contr...
Definition: TrainUnit.h:419
SequTypeForRepeatEntry
@ SequTypeForRepeatEntry
Definition: TrainUnit.h:75
TRailGraphics::bmName
Graphics::TBitmap * bmName
Definition: GraphicUnit.h:524
clB0G0R0
#define clB0G0R0
Definition: GraphicUnit.h:36
Buffers
@ Buffers
Definition: TrackUnit.h:65
TActionVectorEntry::RearStartOrRepeatMins
int RearStartOrRepeatMins
Definition: TrainUnit.h:132
clFrontCodeTimetable
#define clFrontCodeTimetable
Definition: GraphicUnit.h:296
TActionVectorEntry::OtherHeadCode
AnsiString OtherHeadCode
Definition: TrainUnit.h:122